mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-28 16:51:48 +02:00
Revise aggregate functions per earlier discussions in pghackers.
There's now only one transition value and transition function. NULL handling in aggregates is a lot cleaner. Also, use Numeric accumulators instead of integer accumulators for sum/avg on integer datatypes --- this avoids overflow at the cost of being a little slower. Implement VARIANCE() and STDDEV() aggregates in the standard backend. Also, enable new LIKE selectivity estimators by default. Unrelated change, but as long as I had to force initdb anyway...
This commit is contained in:
parent
139f19c302
commit
bec98a31c5
@ -1,6 +1,6 @@
|
||||
.\" This is -*-nroff-*-
|
||||
.\" XXX standard disclaimer belongs here....
|
||||
.\" $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.6 2000/06/09 01:43:56 momjian Exp $
|
||||
.\" $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.7 2000/07/17 03:04:40 tgl Exp $
|
||||
.TH "SYSTEM CATALOGS" INTRO 03/13/94 PostgreSQL PostgreSQL
|
||||
.SH "Section 7 - System Catalogs"
|
||||
.de LS
|
||||
@ -91,20 +91,16 @@ The following catalogs relate to the class/type system.
|
||||
* see DEFINE AGGREGATE for an explanation of transition functions
|
||||
*/
|
||||
pg_aggregate
|
||||
NameData aggname /* aggregate name (e.g., "count") */
|
||||
NameData aggname /* aggregate name (e.g., "count") */
|
||||
oid aggowner /* usesysid of creator */
|
||||
regproc aggtransfn1 /* first transition function */
|
||||
regproc aggtransfn2 /* second transition function */
|
||||
regproc aggtransfn /* transition function */
|
||||
regproc aggfinalfn /* final function */
|
||||
oid aggbasetype /* type of data on which aggregate
|
||||
operates */
|
||||
oid aggtranstype1 /* type returned by aggtransfn1 */
|
||||
oid aggtranstype2 /* type returned by aggtransfn2 */
|
||||
oid aggfinaltype /* type returned by aggfinalfn */
|
||||
text agginitval1 /* external format of initial
|
||||
(starting) value of aggtransfn1 */
|
||||
text agginitval2 /* external format of initial
|
||||
(starting) value of aggtransfn2 */
|
||||
oid aggtranstype /* type of aggregate's transition
|
||||
(state) data */
|
||||
oid aggfinaltype /* type of aggregate's final result */
|
||||
text agginitval /* external format of initial state value */
|
||||
.fi
|
||||
.nf M
|
||||
pg_am
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_aggregate.sgml,v 1.9 2000/03/31 14:57:05 tgl Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_aggregate.sgml,v 1.10 2000/07/17 03:04:41 tgl Exp $
|
||||
Postgres documentation
|
||||
-->
|
||||
|
||||
@ -21,20 +21,18 @@ Postgres documentation
|
||||
</refnamediv>
|
||||
<refsynopsisdiv>
|
||||
<refsynopsisdivinfo>
|
||||
<date>1999-07-20</date>
|
||||
<date>2000-07-16</date>
|
||||
</refsynopsisdivinfo>
|
||||
<synopsis>
|
||||
CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( BASETYPE = <replaceable class="PARAMETER">input_data_type</replaceable>
|
||||
[ , SFUNC1 = <replaceable class="PARAMETER">sfunc1</replaceable>, STYPE1 = <replaceable class="PARAMETER">state1_type</replaceable> ]
|
||||
[ , SFUNC2 = <replaceable class="PARAMETER">sfunc2</replaceable>, STYPE2 = <replaceable class="PARAMETER">state2_type</replaceable> ]
|
||||
CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( BASETYPE = <replaceable class="PARAMETER">input_data_type</replaceable>,
|
||||
SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>, STYPE = <replaceable class="PARAMETER">state_type</replaceable>
|
||||
[ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
|
||||
[ , INITCOND1 = <replaceable class="PARAMETER">initial_condition1</replaceable> ]
|
||||
[ , INITCOND2 = <replaceable class="PARAMETER">initial_condition2</replaceable> ] )
|
||||
[ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ] )
|
||||
</synopsis>
|
||||
|
||||
<refsect2 id="R2-SQL-CREATEAGGREGATE-1">
|
||||
<refsect2info>
|
||||
<date>1998-09-09</date>
|
||||
<date>2000-07-16</date>
|
||||
</refsect2info>
|
||||
<title>
|
||||
Inputs
|
||||
@ -55,57 +53,39 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( BASETYPE =
|
||||
<listitem>
|
||||
<para>
|
||||
The input data type on which this aggregate function operates.
|
||||
This can be specified as ANY for an aggregate that does not
|
||||
examine its input values
|
||||
(an example is <function>count(*)</function>).
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">sfunc1</replaceable></term>
|
||||
<term><replaceable class="PARAMETER">sfunc</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
A state transition function
|
||||
to be called for every non-NULL input data value.
|
||||
This must be a function of two arguments, the first being of
|
||||
type <replaceable class="PARAMETER">state1_type</replaceable>
|
||||
The name of the state transition function
|
||||
to be called for each input data value.
|
||||
This is normally a function of two arguments, the first being of
|
||||
type <replaceable class="PARAMETER">state_type</replaceable>
|
||||
and the second of
|
||||
type <replaceable class="PARAMETER">input_data_type</replaceable>.
|
||||
The function must return a value of
|
||||
type <replaceable class="PARAMETER">state1_type</replaceable>.
|
||||
This function takes the current state value 1 and the current
|
||||
input data item, and returns the next state value 1.
|
||||
Alternatively, for an aggregate that does not examine its input
|
||||
values, the function takes just one argument of
|
||||
type <replaceable class="PARAMETER">state_type</replaceable>.
|
||||
In either case the function must return a value of
|
||||
type <replaceable class="PARAMETER">state_type</replaceable>.
|
||||
This function takes the current state value and the current
|
||||
input data item, and returns the next state value.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">state1_type</replaceable></term>
|
||||
<term><replaceable class="PARAMETER">state_type</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The data type for the first state value of the aggregate.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">sfunc2</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
A state transition function
|
||||
to be called for every non-NULL input data value.
|
||||
This must be a function of one argument of
|
||||
type <replaceable class="PARAMETER">state2_type</replaceable>,
|
||||
returning a value of the same type.
|
||||
This function takes the current state value 2 and
|
||||
returns the next state value 2.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">state2_type</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The data type for the second state value of the aggregate.
|
||||
The data type for the aggregate's state value.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -114,35 +94,28 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( BASETYPE =
|
||||
<term><replaceable class="PARAMETER">ffunc</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The final function called to compute the aggregate's result
|
||||
after all input data has been traversed.
|
||||
If both state values are used, the final function must
|
||||
take two arguments of types
|
||||
<replaceable class="PARAMETER">state1_type</replaceable>
|
||||
and
|
||||
<replaceable class="PARAMETER">state2_type</replaceable>.
|
||||
If only one state value is used, the final function must
|
||||
take a single argument of that state value's type.
|
||||
The name of the final function called to compute the aggregate's
|
||||
result after all input data has been traversed. The function
|
||||
must take a single argument of type
|
||||
<replaceable class="PARAMETER">state_type</replaceable>.
|
||||
The output datatype of the aggregate is defined as the return
|
||||
type of this function.
|
||||
If <replaceable class="PARAMETER">ffunc</replaceable>
|
||||
is not specified, then the ending state value is used as the
|
||||
aggregate's result, and the output type is
|
||||
<replaceable class="PARAMETER">state_type</replaceable>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">initial_condition1</replaceable></term>
|
||||
<term><replaceable class="PARAMETER">initial_condition</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The initial value for state value 1.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><replaceable class="PARAMETER">initial_condition2</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The initial value for state value 2.
|
||||
The initial setting for the state value. This must be a literal
|
||||
constant in the form accepted for the datatype
|
||||
<replaceable class="PARAMETER">state_type</replaceable>.
|
||||
If not specified, the state value starts out NULL.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -177,7 +150,7 @@ CREATE
|
||||
|
||||
<refsect1 id="R1-SQL-CREATEAGGREGATE-1">
|
||||
<refsect1info>
|
||||
<date>1998-09-09</date>
|
||||
<date>2000-07-16</date>
|
||||
</refsect1info>
|
||||
<title>
|
||||
Description
|
||||
@ -199,65 +172,76 @@ CREATE
|
||||
of the same name and input data type as an aggregate.
|
||||
</para>
|
||||
<para>
|
||||
An aggregate function is made from between one and three ordinary
|
||||
An aggregate function is made from one or two ordinary
|
||||
functions:
|
||||
two state transition functions,
|
||||
<replaceable class="PARAMETER">sfunc1</replaceable>
|
||||
and <replaceable class="PARAMETER">sfunc2</replaceable>,
|
||||
and a final calculation function,
|
||||
a state transition function
|
||||
<replaceable class="PARAMETER">sfunc</replaceable>,
|
||||
and an optional final calculation function
|
||||
<replaceable class="PARAMETER">ffunc</replaceable>.
|
||||
These are used as follows:
|
||||
<programlisting>
|
||||
<replaceable class="PARAMETER">sfunc1</replaceable>( internal-state1, next-data-item ) ---> next-internal-state1
|
||||
<replaceable class="PARAMETER">sfunc2</replaceable>( internal-state2 ) ---> next-internal-state2
|
||||
<replaceable class="PARAMETER">ffunc</replaceable>(internal-state1, internal-state2) ---> aggregate-value
|
||||
<replaceable class="PARAMETER">sfunc</replaceable>( internal-state, next-data-item ) ---> next-internal-state
|
||||
<replaceable class="PARAMETER">ffunc</replaceable>( internal-state ) ---> aggregate-value
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
<productname>Postgres</productname> creates one or two temporary variables
|
||||
(of data types <replaceable class="PARAMETER">stype1</replaceable> and/or
|
||||
<replaceable class="PARAMETER">stype2</replaceable>) to hold the
|
||||
current internal states of the aggregate. At each input data item,
|
||||
the state transition function(s) are invoked to calculate new values
|
||||
for the internal state values. After all the data has been processed,
|
||||
<productname>Postgres</productname> creates a temporary variable
|
||||
of data type <replaceable class="PARAMETER">stype</replaceable>
|
||||
to hold the current internal state of the aggregate. At each input
|
||||
data item,
|
||||
the state transition function is invoked to calculate a new
|
||||
internal state value. After all the data has been processed,
|
||||
the final function is invoked once to calculate the aggregate's output
|
||||
value.
|
||||
</para>
|
||||
<para>
|
||||
<replaceable class="PARAMETER">ffunc</replaceable> must be specified if
|
||||
both transition functions are specified. If only one transition function
|
||||
is used, then <replaceable class="PARAMETER">ffunc</replaceable> is
|
||||
optional. The default behavior when
|
||||
<replaceable class="PARAMETER">ffunc</replaceable> is not provided is
|
||||
to return the ending value of the internal state value being used
|
||||
(and, therefore, the aggregate's output type is the same as that
|
||||
state value's type).
|
||||
value. If there is no final function then the ending state value
|
||||
is returned as-is.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
An aggregate function may also provide one or two initial conditions,
|
||||
that is, initial values for the internal state values being used.
|
||||
These are specified and stored in the database as fields of type
|
||||
<type>text</type>, but they must be valid external representations
|
||||
of constants of the state value datatypes. If
|
||||
<replaceable class="PARAMETER">sfunc1</replaceable> is specified
|
||||
without an <replaceable class="PARAMETER">initcond1</replaceable> value,
|
||||
then the system does not call
|
||||
<replaceable class="PARAMETER">sfunc1</replaceable>
|
||||
at the first input item; instead, the internal state value 1 is
|
||||
initialized with the first input value, and
|
||||
<replaceable class="PARAMETER">sfunc1</replaceable> is called beginning
|
||||
at the second input item. This is useful for aggregates like MIN and
|
||||
MAX. Note that an aggregate using this feature will return NULL when
|
||||
called with no input values. There is no comparable provision for
|
||||
state value 2; if <replaceable class="PARAMETER">sfunc2</replaceable> is
|
||||
specified then an <replaceable class="PARAMETER">initcond2</replaceable> is
|
||||
required.
|
||||
An aggregate function may provide an initial condition,
|
||||
that is, an initial value for the internal state value.
|
||||
This is specified and stored in the database as a field of type
|
||||
<type>text</type>, but it must be a valid external representation
|
||||
of a constant of the state value datatype. If it is not supplied
|
||||
then the state value starts out NULL.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the state transition function is declared "strict" in pg_proc,
|
||||
then it cannot be called with NULL inputs. With such a transition
|
||||
function, aggregate execution behaves as follows. NULL input 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
|
||||
non-NULL input value replaces the state value, and the transition
|
||||
function is invoked beginning with the second non-NULL input value.
|
||||
This is handy for implementing aggregates like <function>max</function>.
|
||||
Note that this behavior is only available when
|
||||
<replaceable class="PARAMETER">state_type</replaceable>
|
||||
is the same as
|
||||
<replaceable class="PARAMETER">input_data_type</replaceable>.
|
||||
When these types are different, you must supply a non-NULL initial
|
||||
condition or use a non-strict transition function.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the state transition function is not strict, then it will be called
|
||||
unconditionally at each input value, and must deal with NULL inputs
|
||||
and NULL transition values for itself. This allows the aggregate
|
||||
author to have full control over the aggregate's handling of NULLs.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the final function is declared "strict", then it will not
|
||||
be called when the ending state value is NULL; instead a NULL result
|
||||
will be output automatically. (Of course this is just the normal
|
||||
behavior of strict functions.) In any case the final function has
|
||||
the option of returning NULL. For example, the final function for
|
||||
<function>avg</function> returns NULL when it sees there were zero
|
||||
input tuples.
|
||||
</para>
|
||||
|
||||
<refsect2 id="R2-SQL-CREATEAGGREGATE-3">
|
||||
<refsect2info>
|
||||
<date>1998-09-09</date>
|
||||
<date>2000-07-16</date>
|
||||
</refsect2info>
|
||||
<title>
|
||||
Notes
|
||||
@ -272,29 +256,6 @@ CREATE
|
||||
in any order, not just the order illustrated above.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It is possible to specify aggregate functions
|
||||
that have varying combinations of state and final functions.
|
||||
For example, the <function>count</function> aggregate requires
|
||||
<replaceable class="PARAMETER">sfunc2</replaceable>
|
||||
(an incrementing function) but not
|
||||
<replaceable class="PARAMETER">sfunc1</replaceable> or
|
||||
<replaceable class="PARAMETER">ffunc</replaceable>,
|
||||
whereas the <function>sum</function> aggregate requires
|
||||
<replaceable class="PARAMETER">sfunc1</replaceable> (an addition
|
||||
function) but not <replaceable class="PARAMETER">sfunc2</replaceable> or
|
||||
<replaceable class="PARAMETER">ffunc</replaceable>, and the
|
||||
<function>avg</function>
|
||||
aggregate requires
|
||||
both state functions as
|
||||
well as a <replaceable class="PARAMETER">ffunc</replaceable> (a division
|
||||
function) to produce its
|
||||
answer. In any case, at least one state function must be
|
||||
defined, and any <replaceable class="PARAMETER">sfunc2</replaceable> must
|
||||
have a corresponding
|
||||
<replaceable class="PARAMETER">initcond2</replaceable>.
|
||||
</para>
|
||||
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.7 2000/05/18 14:24:32 momjian Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_aggregate.sgml,v 1.8 2000/07/17 03:04:41 tgl Exp $
|
||||
Postgres documentation
|
||||
-->
|
||||
|
||||
@ -49,7 +49,7 @@ DROP AGGREGATE <replaceable class="PARAMETER">name</replaceable> <replaceable cl
|
||||
<para>
|
||||
The type of an existing aggregate function.
|
||||
(Refer to the <citetitle>PostgreSQL User's Guide</citetitle> for
|
||||
further information about data types).
|
||||
further information about data types.)
|
||||
<comment>This should become a cross-reference rather than a
|
||||
hard-coded chapter number</comment>
|
||||
</para>
|
||||
@ -80,7 +80,7 @@ DROP
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><computeroutput>
|
||||
NOTICE RemoveAggregate: aggregate '<replaceable class="parameter">agg</replaceable>' for '<replaceable class="parameter">type</replaceable>' does not exist
|
||||
ERROR: RemoveAggregate: aggregate '<replaceable class="parameter">agg</replaceable>' for '<replaceable class="parameter">type</replaceable>' does not exist
|
||||
</computeroutput></term>
|
||||
<listitem>
|
||||
<para>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/xaggr.sgml,v 1.7 2000/03/31 03:27:41 thomas Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/xaggr.sgml,v 1.8 2000/07/17 03:04:40 tgl Exp $
|
||||
-->
|
||||
|
||||
<chapter id="xaggr">
|
||||
@ -16,39 +16,20 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xaggr.sgml,v 1.7 2000/03/31 03:27:41 thomas
|
||||
an initial value for the state, and a state transition
|
||||
function. The state transition function is just an
|
||||
ordinary function that could also be used outside the
|
||||
context of the aggregate.
|
||||
context of the aggregate. A <firstterm>final function</firstterm>
|
||||
can also be specified, in case the desired output of the aggregate
|
||||
is different from the data that needs to be kept in the running
|
||||
state value.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Actually, in order to make it easier to construct useful
|
||||
aggregates from existing functions, an aggregate can have
|
||||
one or two separate state values, one or two transition
|
||||
functions to update those state values, and a
|
||||
<firstterm>final function</firstterm> that computes the
|
||||
actual aggregate result from the ending state values.
|
||||
Thus, in addition to the input and result datatypes seen by a user
|
||||
of the aggregate, there is an internal state-value datatype that
|
||||
may be different from both the input and result types.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Thus there can be as many as four datatypes involved:
|
||||
the type of the input data items, the type of the aggregate's
|
||||
result, and the types of the two state values. Only the
|
||||
input and result datatypes are seen by a user of the aggregate.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Some state transition functions need to look at each successive
|
||||
input to compute the next state value, while others ignore the
|
||||
specific input value and simply update their internal state.
|
||||
(The most useful example of the second kind is a running count
|
||||
of the number of input items.) The <productname>Postgres</productname>
|
||||
aggregate machinery defines <acronym>sfunc1</acronym> for
|
||||
an aggregate as a function that is passed both the old state
|
||||
value and the current input value, while <acronym>sfunc2</acronym>
|
||||
is a function that is passed only the old state value.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If we define an aggregate that uses only <acronym>sfunc1</acronym>,
|
||||
If we define an aggregate that does not use a final function,
|
||||
we have an aggregate that computes a running function of
|
||||
the attribute values from each instance. "Sum" is an
|
||||
example of this kind of aggregate. "Sum" starts at
|
||||
@ -60,10 +41,10 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xaggr.sgml,v 1.7 2000/03/31 03:27:41 thomas
|
||||
|
||||
<programlisting>
|
||||
CREATE AGGREGATE complex_sum (
|
||||
sfunc1 = complex_add,
|
||||
sfunc = complex_add,
|
||||
basetype = complex,
|
||||
stype1 = complex,
|
||||
initcond1 = '(0,0)'
|
||||
stype = complex,
|
||||
initcond = '(0,0)'
|
||||
);
|
||||
|
||||
SELECT complex_sum(a) FROM test_complex;
|
||||
@ -81,67 +62,48 @@ SELECT complex_sum(a) FROM test_complex;
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If we define only <acronym>sfunc2</acronym>, we are
|
||||
specifying an aggregate
|
||||
that computes a running function that is independent of
|
||||
the attribute values from each instance.
|
||||
"Count" is the most common example of this kind of
|
||||
aggregate. "Count" starts at zero and adds one to its
|
||||
running total for each instance, ignoring the instance
|
||||
value. Here, we use the built-in
|
||||
<acronym>int4inc</acronym> routine to do
|
||||
the work for us. This routine increments (adds one to)
|
||||
its argument.
|
||||
|
||||
<programlisting>
|
||||
CREATE AGGREGATE my_count (
|
||||
sfunc2 = int4inc, -- add one
|
||||
basetype = int4,
|
||||
stype2 = int4,
|
||||
initcond2 = '0'
|
||||
);
|
||||
|
||||
SELECT my_count(*) as emp_count from EMP;
|
||||
|
||||
+----------+
|
||||
|emp_count |
|
||||
+----------+
|
||||
|5 |
|
||||
+----------+
|
||||
</programlisting>
|
||||
The above definition of "Sum" will return zero (the initial
|
||||
state condition) if there are no non-null input values.
|
||||
Perhaps we want to return NULL in that case instead --- SQL92
|
||||
expects "Sum" to behave that way. We can do this simply by
|
||||
omitting the "initcond" phrase, so that the initial state
|
||||
condition is NULL. Ordinarily this would mean that the sfunc
|
||||
would need to check for a NULL state-condition input, but for
|
||||
"Sum" and some other simple aggregates like "Max" and "Min",
|
||||
it's sufficient to insert the first non-null input value into
|
||||
the state variable and then start applying the transition function
|
||||
at the second non-null input value. <productname>Postgres</productname>
|
||||
will do that automatically if the initial condition is NULL and
|
||||
the transition function is marked "strict" (ie, not to be called
|
||||
for NULL inputs).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
"Average" is an example of an aggregate that requires
|
||||
both a function to compute the running sum and a function
|
||||
to compute the running count. When all of the
|
||||
instances have been processed, the final answer for the
|
||||
aggregate is the running sum divided by the running
|
||||
count. We use the <acronym>int4pl</acronym> and
|
||||
<acronym>int4inc</acronym> routines we used
|
||||
before as well as the <productname>Postgres</productname> integer division
|
||||
routine, <acronym>int4div</acronym>, to compute the division of the sum by
|
||||
the count.
|
||||
Another bit of default behavior for a "strict" transition function
|
||||
is that the previous state value is retained unchanged whenever a
|
||||
NULL input value is encountered. Thus, NULLs are ignored. If you
|
||||
need some other behavior for NULL inputs, just define your transition
|
||||
function as non-strict, and code it to test for NULL inputs and do
|
||||
whatever is needed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
"Average" is a more complex example of an aggregate. It requires
|
||||
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
|
||||
these quantities. Average is typically implemented by using a
|
||||
two-element array as the transition state value. For example,
|
||||
the built-in implementation of <function>avg(float8)</function>
|
||||
looks like:
|
||||
|
||||
<programlisting>
|
||||
CREATE AGGREGATE my_average (
|
||||
sfunc1 = int4pl, -- sum
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
sfunc2 = int4inc, -- count
|
||||
stype2 = int4,
|
||||
finalfunc = int4div, -- division
|
||||
initcond1 = '0',
|
||||
initcond2 = '0'
|
||||
CREATE AGGREGATE avg (
|
||||
sfunc = float8_accum,
|
||||
basetype = float8,
|
||||
stype = _float8,
|
||||
finalfunc = float8_avg,
|
||||
initcond = '{0,0}'
|
||||
);
|
||||
|
||||
SELECT my_average(salary) as emp_average FROM EMP;
|
||||
|
||||
+------------+
|
||||
|emp_average |
|
||||
+------------+
|
||||
|1640 |
|
||||
+------------+
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.34 2000/07/05 23:11:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.35 2000/07/17 03:04:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -21,6 +21,8 @@
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
@ -36,13 +38,7 @@
|
||||
* Currently, redefining aggregates using the same name is not
|
||||
* supported. In such a case, a warning is printed that the
|
||||
* aggregate already exists. If such is not the case, a new tuple
|
||||
* is created and inserted in the aggregate relation. The fields
|
||||
* of this tuple are aggregate name, owner id, 2 transition functions
|
||||
* (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
|
||||
* type of data on which aggtransfn1 operates (aggbasetype), return
|
||||
* types of the two transition functions (aggtranstype1 and
|
||||
* aggtranstype2), final return type (aggfinaltype), and initial values
|
||||
* for the two state transition functions (agginitval1 and agginitval2).
|
||||
* is created and inserted in the aggregate relation.
|
||||
* All types and functions must have been defined
|
||||
* prior to defining the aggregate.
|
||||
*
|
||||
@ -50,31 +46,27 @@
|
||||
*/
|
||||
void
|
||||
AggregateCreate(char *aggName,
|
||||
char *aggtransfn1Name,
|
||||
char *aggtransfn2Name,
|
||||
char *aggtransfnName,
|
||||
char *aggfinalfnName,
|
||||
char *aggbasetypeName,
|
||||
char *aggtransfn1typeName,
|
||||
char *aggtransfn2typeName,
|
||||
char *agginitval1,
|
||||
char *agginitval2)
|
||||
char *aggtranstypeName,
|
||||
char *agginitval)
|
||||
{
|
||||
int i;
|
||||
Relation aggdesc;
|
||||
HeapTuple tup;
|
||||
char nulls[Natts_pg_aggregate];
|
||||
Datum values[Natts_pg_aggregate];
|
||||
Form_pg_proc proc;
|
||||
Oid xfn1 = InvalidOid;
|
||||
Oid xfn2 = InvalidOid;
|
||||
Oid ffn = InvalidOid;
|
||||
Oid xbase = InvalidOid;
|
||||
Oid xret1 = InvalidOid;
|
||||
Oid xret2 = InvalidOid;
|
||||
Oid fret = InvalidOid;
|
||||
Oid transfn;
|
||||
Oid finalfn = InvalidOid; /* can be omitted */
|
||||
Oid basetype;
|
||||
Oid transtype;
|
||||
Oid finaltype;
|
||||
Oid fnArgs[FUNC_MAX_ARGS];
|
||||
int nargs;
|
||||
NameData aname;
|
||||
TupleDesc tupDesc;
|
||||
int i;
|
||||
|
||||
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
|
||||
@ -82,143 +74,112 @@ AggregateCreate(char *aggName,
|
||||
if (!aggName)
|
||||
elog(ERROR, "AggregateCreate: no aggregate name supplied");
|
||||
|
||||
if (!aggtransfn1Name && !aggtransfn2Name)
|
||||
elog(ERROR, "AggregateCreate: aggregate must have at least one transition function");
|
||||
if (!aggtransfnName)
|
||||
elog(ERROR, "AggregateCreate: aggregate must have a transition function");
|
||||
|
||||
if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
|
||||
elog(ERROR, "AggregateCreate: Aggregate must have final function with both transition functions");
|
||||
|
||||
/* handle the aggregate's base type (input data type) */
|
||||
/*
|
||||
* Handle the aggregate's base type (input data type). This can be
|
||||
* specified as 'ANY' for a data-independent transition function,
|
||||
* such as COUNT(*).
|
||||
*/
|
||||
tup = SearchSysCacheTuple(TYPENAME,
|
||||
PointerGetDatum(aggbasetypeName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "AggregateCreate: Type '%s' undefined", aggbasetypeName);
|
||||
xbase = tup->t_data->t_oid;
|
||||
if (HeapTupleIsValid(tup))
|
||||
{
|
||||
basetype = tup->t_data->t_oid;
|
||||
Assert(OidIsValid(basetype));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcasecmp(aggbasetypeName, "ANY") != 0)
|
||||
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
||||
aggbasetypeName);
|
||||
basetype = InvalidOid;
|
||||
}
|
||||
|
||||
/* make sure there is no existing agg of same name and base type */
|
||||
tup = SearchSysCacheTuple(AGGNAME,
|
||||
PointerGetDatum(aggName),
|
||||
ObjectIdGetDatum(xbase),
|
||||
ObjectIdGetDatum(basetype),
|
||||
0, 0);
|
||||
if (HeapTupleIsValid(tup))
|
||||
elog(ERROR,
|
||||
"AggregateCreate: aggregate '%s' with base type '%s' already exists",
|
||||
aggName, aggbasetypeName);
|
||||
|
||||
/* handle transfn1 and transtype1 */
|
||||
if (aggtransfn1Name)
|
||||
{
|
||||
tup = SearchSysCacheTuple(TYPENAME,
|
||||
PointerGetDatum(aggtransfn1typeName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
||||
aggtransfn1typeName);
|
||||
xret1 = tup->t_data->t_oid;
|
||||
/* handle transtype */
|
||||
tup = SearchSysCacheTuple(TYPENAME,
|
||||
PointerGetDatum(aggtranstypeName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
||||
aggtranstypeName);
|
||||
transtype = tup->t_data->t_oid;
|
||||
Assert(OidIsValid(transtype));
|
||||
|
||||
fnArgs[0] = xret1;
|
||||
fnArgs[1] = xbase;
|
||||
tup = SearchSysCacheTuple(PROCNAME,
|
||||
PointerGetDatum(aggtransfn1Name),
|
||||
Int32GetDatum(2),
|
||||
PointerGetDatum(fnArgs),
|
||||
0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "AggregateCreate: '%s('%s', '%s') does not exist",
|
||||
aggtransfn1Name, aggtransfn1typeName, aggbasetypeName);
|
||||
if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
|
||||
elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
|
||||
aggtransfn1Name, aggtransfn1typeName);
|
||||
xfn1 = tup->t_data->t_oid;
|
||||
if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
|
||||
!OidIsValid(xbase))
|
||||
elog(ERROR, "AggregateCreate: bogus function '%s'", aggtransfn1Name);
|
||||
/* handle transfn */
|
||||
fnArgs[0] = transtype;
|
||||
if (OidIsValid(basetype))
|
||||
{
|
||||
fnArgs[1] = basetype;
|
||||
nargs = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
nargs = 1;
|
||||
}
|
||||
tup = SearchSysCacheTuple(PROCNAME,
|
||||
PointerGetDatum(aggtransfnName),
|
||||
Int32GetDatum(nargs),
|
||||
PointerGetDatum(fnArgs),
|
||||
0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
|
||||
transfn = tup->t_data->t_oid;
|
||||
proc = (Form_pg_proc) GETSTRUCT(tup);
|
||||
if (proc->prorettype != transtype)
|
||||
elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
|
||||
aggtransfnName, aggtranstypeName);
|
||||
Assert(OidIsValid(transfn));
|
||||
/*
|
||||
* If the transfn is strict and the initval is NULL, make sure
|
||||
* input type and transtype are the same (or at least binary-
|
||||
* compatible), so that it's OK to use the first input value
|
||||
* as the initial transValue.
|
||||
*/
|
||||
if (((Form_pg_proc) GETSTRUCT(tup))->proisstrict && agginitval == NULL)
|
||||
{
|
||||
if (basetype != transtype &&
|
||||
! IS_BINARY_COMPATIBLE(basetype, transtype))
|
||||
elog(ERROR, "AggregateCreate: must not omit initval when transfn is strict and transtype is not compatible with input type");
|
||||
}
|
||||
|
||||
/* handle transfn2 and transtype2 */
|
||||
if (aggtransfn2Name)
|
||||
/* handle finalfn, if supplied */
|
||||
if (aggfinalfnName)
|
||||
{
|
||||
tup = SearchSysCacheTuple(TYPENAME,
|
||||
PointerGetDatum(aggtransfn2typeName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "AggregateCreate: Type '%s' undefined",
|
||||
aggtransfn2typeName);
|
||||
xret2 = tup->t_data->t_oid;
|
||||
|
||||
fnArgs[0] = xret2;
|
||||
fnArgs[0] = transtype;
|
||||
fnArgs[1] = 0;
|
||||
tup = SearchSysCacheTuple(PROCNAME,
|
||||
PointerGetDatum(aggtransfn2Name),
|
||||
PointerGetDatum(aggfinalfnName),
|
||||
Int32GetDatum(1),
|
||||
PointerGetDatum(fnArgs),
|
||||
0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "AggregateCreate: '%s'('%s') does not exist",
|
||||
aggtransfn2Name, aggtransfn2typeName);
|
||||
if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
|
||||
elog(ERROR, "AggregateCreate: return type of '%s' is not '%s'",
|
||||
aggtransfn2Name, aggtransfn2typeName);
|
||||
xfn2 = tup->t_data->t_oid;
|
||||
if (!OidIsValid(xfn2) || !OidIsValid(xret2))
|
||||
elog(ERROR, "AggregateCreate: bogus function '%s'", aggtransfn2Name);
|
||||
}
|
||||
|
||||
/* handle finalfn */
|
||||
if (aggfinalfnName)
|
||||
{
|
||||
int nargs = 0;
|
||||
|
||||
if (OidIsValid(xret1))
|
||||
fnArgs[nargs++] = xret1;
|
||||
if (OidIsValid(xret2))
|
||||
fnArgs[nargs++] = xret2;
|
||||
fnArgs[nargs] = 0; /* make sure slot 2 is empty if just 1 arg */
|
||||
tup = SearchSysCacheTuple(PROCNAME,
|
||||
PointerGetDatum(aggfinalfnName),
|
||||
Int32GetDatum(nargs),
|
||||
PointerGetDatum(fnArgs),
|
||||
0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
if (nargs == 2)
|
||||
elog(ERROR, "AggregateCreate: '%s'('%s','%s') does not exist",
|
||||
aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
|
||||
else if (OidIsValid(xret1))
|
||||
elog(ERROR, "AggregateCreate: '%s'('%s') does not exist",
|
||||
aggfinalfnName, aggtransfn1typeName);
|
||||
else
|
||||
elog(ERROR, "AggregateCreate: '%s'('%s') does not exist",
|
||||
aggfinalfnName, aggtransfn2typeName);
|
||||
}
|
||||
ffn = tup->t_data->t_oid;
|
||||
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
|
||||
finalfn = tup->t_data->t_oid;
|
||||
proc = (Form_pg_proc) GETSTRUCT(tup);
|
||||
fret = proc->prorettype;
|
||||
if (!OidIsValid(ffn) || !OidIsValid(fret))
|
||||
elog(ERROR, "AggregateCreate: bogus function '%s'", aggfinalfnName);
|
||||
finaltype = proc->prorettype;
|
||||
Assert(OidIsValid(finalfn));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* If no finalfn, aggregate result type is type of the sole state
|
||||
* value (we already checked there is only one)
|
||||
* If no finalfn, aggregate result type is type of the state value
|
||||
*/
|
||||
if (OidIsValid(xret1))
|
||||
fret = xret1;
|
||||
else
|
||||
fret = xret2;
|
||||
finaltype = transtype;
|
||||
}
|
||||
Assert(OidIsValid(fret));
|
||||
|
||||
/*
|
||||
* If transition function 2 is defined, it must have an initial value,
|
||||
* whereas transition function 1 need not, which allows max and min
|
||||
* aggregates to return NULL if they are evaluated on empty sets.
|
||||
*/
|
||||
if (OidIsValid(xfn2) && !agginitval2)
|
||||
elog(ERROR, "AggregateCreate: transition function 2 MUST have an initial value");
|
||||
Assert(OidIsValid(finaltype));
|
||||
|
||||
/* initialize nulls and values */
|
||||
for (i = 0; i < Natts_pg_aggregate; i++)
|
||||
@ -229,25 +190,17 @@ AggregateCreate(char *aggName,
|
||||
namestrcpy(&aname, aggName);
|
||||
values[Anum_pg_aggregate_aggname - 1] = NameGetDatum(&aname);
|
||||
values[Anum_pg_aggregate_aggowner - 1] = Int32GetDatum(GetUserId());
|
||||
values[Anum_pg_aggregate_aggtransfn1 - 1] = ObjectIdGetDatum(xfn1);
|
||||
values[Anum_pg_aggregate_aggtransfn2 - 1] = ObjectIdGetDatum(xfn2);
|
||||
values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(ffn);
|
||||
values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(xbase);
|
||||
values[Anum_pg_aggregate_aggtranstype1 - 1] = ObjectIdGetDatum(xret1);
|
||||
values[Anum_pg_aggregate_aggtranstype2 - 1] = ObjectIdGetDatum(xret2);
|
||||
values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(fret);
|
||||
values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
|
||||
values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
|
||||
values[Anum_pg_aggregate_aggbasetype - 1] = ObjectIdGetDatum(basetype);
|
||||
values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(transtype);
|
||||
values[Anum_pg_aggregate_aggfinaltype - 1] = ObjectIdGetDatum(finaltype);
|
||||
|
||||
if (agginitval1)
|
||||
values[Anum_pg_aggregate_agginitval1 - 1] =
|
||||
DirectFunctionCall1(textin, CStringGetDatum(agginitval1));
|
||||
if (agginitval)
|
||||
values[Anum_pg_aggregate_agginitval - 1] =
|
||||
DirectFunctionCall1(textin, CStringGetDatum(agginitval));
|
||||
else
|
||||
nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
|
||||
|
||||
if (agginitval2)
|
||||
values[Anum_pg_aggregate_agginitval2 - 1] =
|
||||
DirectFunctionCall1(textin, CStringGetDatum(agginitval2));
|
||||
else
|
||||
nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
|
||||
nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
|
||||
|
||||
aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
|
||||
tupDesc = aggdesc->rd_att;
|
||||
@ -271,11 +224,9 @@ AggregateCreate(char *aggName,
|
||||
}
|
||||
|
||||
Datum
|
||||
AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
|
||||
AggNameGetInitVal(char *aggName, Oid basetype, bool *isNull)
|
||||
{
|
||||
HeapTuple tup;
|
||||
Relation aggRel;
|
||||
int initValAttno;
|
||||
Oid transtype,
|
||||
typinput,
|
||||
typelem;
|
||||
@ -285,15 +236,6 @@ AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
|
||||
|
||||
Assert(PointerIsValid(aggName));
|
||||
Assert(PointerIsValid(isNull));
|
||||
Assert(xfuncno == 1 || xfuncno == 2);
|
||||
|
||||
/*
|
||||
* since we will have to use fastgetattr (in case one or both init
|
||||
* vals are NULL), we will need to open the relation. Do that first
|
||||
* to ensure we don't get a stale tuple from the cache.
|
||||
*/
|
||||
|
||||
aggRel = heap_openr(AggregateRelationName, AccessShareLock);
|
||||
|
||||
tup = SearchSysCacheTuple(AGGNAME,
|
||||
PointerGetDatum(aggName),
|
||||
@ -302,29 +244,19 @@ AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
|
||||
aggName);
|
||||
if (xfuncno == 1)
|
||||
{
|
||||
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
|
||||
initValAttno = Anum_pg_aggregate_agginitval1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* can only be 1 or 2 */
|
||||
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
|
||||
initValAttno = Anum_pg_aggregate_agginitval2;
|
||||
}
|
||||
transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype;
|
||||
|
||||
textInitVal = fastgetattr(tup, initValAttno,
|
||||
RelationGetDescr(aggRel),
|
||||
isNull);
|
||||
/*
|
||||
* initval is potentially null, so don't try to access it as a struct
|
||||
* field. Must do it the hard way with SysCacheGetAttr.
|
||||
*/
|
||||
textInitVal = SysCacheGetAttr(AGGNAME, tup,
|
||||
Anum_pg_aggregate_agginitval,
|
||||
isNull);
|
||||
if (*isNull)
|
||||
{
|
||||
heap_close(aggRel, AccessShareLock);
|
||||
return PointerGetDatum(NULL);
|
||||
}
|
||||
strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
|
||||
return (Datum) 0;
|
||||
|
||||
heap_close(aggRel, AccessShareLock);
|
||||
strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
|
||||
|
||||
tup = SearchSysCacheTuple(TYPEOID,
|
||||
ObjectIdGetDatum(transtype),
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.44 2000/07/03 23:09:33 wieck Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.45 2000/07/17 03:04:44 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@ -484,16 +484,12 @@ DefineOperator(char *oprName,
|
||||
*/
|
||||
void
|
||||
DefineAggregate(char *aggName, List *parameters)
|
||||
|
||||
{
|
||||
char *stepfunc1Name = NULL;
|
||||
char *stepfunc2Name = NULL;
|
||||
char *transfuncName = NULL;
|
||||
char *finalfuncName = NULL;
|
||||
char *baseType = NULL;
|
||||
char *stepfunc1Type = NULL;
|
||||
char *stepfunc2Type = NULL;
|
||||
char *init1 = NULL;
|
||||
char *init2 = NULL;
|
||||
char *transType = NULL;
|
||||
char *initval = NULL;
|
||||
List *pl;
|
||||
|
||||
foreach(pl, parameters)
|
||||
@ -501,47 +497,28 @@ DefineAggregate(char *aggName, List *parameters)
|
||||
DefElem *defel = (DefElem *) lfirst(pl);
|
||||
|
||||
/*
|
||||
* sfunc1
|
||||
* sfunc1, stype1, and initcond1 are accepted as obsolete spellings
|
||||
* for sfunc, stype, initcond.
|
||||
*/
|
||||
if (!strcasecmp(defel->defname, "sfunc1"))
|
||||
stepfunc1Name = defGetString(defel);
|
||||
else if (!strcasecmp(defel->defname, "basetype"))
|
||||
baseType = defGetString(defel);
|
||||
else if (!strcasecmp(defel->defname, "stype1"))
|
||||
{
|
||||
stepfunc1Type = defGetString(defel);
|
||||
|
||||
/*
|
||||
* sfunc2
|
||||
*/
|
||||
}
|
||||
else if (!strcasecmp(defel->defname, "sfunc2"))
|
||||
stepfunc2Name = defGetString(defel);
|
||||
else if (!strcasecmp(defel->defname, "stype2"))
|
||||
{
|
||||
stepfunc2Type = defGetString(defel);
|
||||
|
||||
/*
|
||||
* final
|
||||
*/
|
||||
}
|
||||
else if (!strcasecmp(defel->defname, "finalfunc"))
|
||||
{
|
||||
if (strcasecmp(defel->defname, "sfunc") == 0)
|
||||
transfuncName = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "sfunc1") == 0)
|
||||
transfuncName = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "finalfunc") == 0)
|
||||
finalfuncName = defGetString(defel);
|
||||
|
||||
/*
|
||||
* initial conditions
|
||||
*/
|
||||
}
|
||||
else if (!strcasecmp(defel->defname, "initcond1"))
|
||||
init1 = defGetString(defel);
|
||||
else if (!strcasecmp(defel->defname, "initcond2"))
|
||||
init2 = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "basetype") == 0)
|
||||
baseType = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "stype") == 0)
|
||||
transType = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "stype1") == 0)
|
||||
transType = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "initcond") == 0)
|
||||
initval = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "initcond1") == 0)
|
||||
initval = defGetString(defel);
|
||||
else
|
||||
{
|
||||
elog(NOTICE, "DefineAggregate: attribute \"%s\" not recognized",
|
||||
defel->defname);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -549,31 +526,20 @@ DefineAggregate(char *aggName, List *parameters)
|
||||
*/
|
||||
if (baseType == NULL)
|
||||
elog(ERROR, "Define: \"basetype\" unspecified");
|
||||
if (stepfunc1Name != NULL)
|
||||
{
|
||||
if (stepfunc1Type == NULL)
|
||||
elog(ERROR, "Define: \"stype1\" unspecified");
|
||||
}
|
||||
if (stepfunc2Name != NULL)
|
||||
{
|
||||
if (stepfunc2Type == NULL)
|
||||
elog(ERROR, "Define: \"stype2\" unspecified");
|
||||
}
|
||||
if (transType == NULL)
|
||||
elog(ERROR, "Define: \"stype\" unspecified");
|
||||
if (transfuncName == NULL)
|
||||
elog(ERROR, "Define: \"sfunc\" unspecified");
|
||||
|
||||
/*
|
||||
* Most of the argument-checking is done inside of AggregateCreate
|
||||
*/
|
||||
AggregateCreate(aggName, /* aggregate name */
|
||||
stepfunc1Name, /* first step function name */
|
||||
stepfunc2Name, /* second step function name */
|
||||
finalfuncName, /* final function name */
|
||||
baseType, /* type of object being aggregated */
|
||||
stepfunc1Type, /* return type of first function */
|
||||
stepfunc2Type, /* return type of second function */
|
||||
init1, /* first initial condition */
|
||||
init2); /* second initial condition */
|
||||
|
||||
/* XXX free palloc'd memory */
|
||||
AggregateCreate(aggName, /* aggregate name */
|
||||
transfuncName, /* step function name */
|
||||
finalfuncName, /* final function name */
|
||||
baseType, /* type of data being aggregated */
|
||||
transType, /* transition data type */
|
||||
initval); /* initial condition */
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.63 2000/07/05 23:11:11 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.64 2000/07/17 03:04:44 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -26,6 +26,7 @@
|
||||
#include "commands/user.h"
|
||||
#include "libpq/crypt.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/syscache.h"
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.73 2000/07/12 02:37:00 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.74 2000/07/17 03:04:51 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -40,6 +40,7 @@
|
||||
#include "executor/execdebug.h"
|
||||
#include "executor/functions.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/fcache2.h"
|
||||
|
@ -12,16 +12,19 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.12 2000/07/12 02:37:01 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.13 2000/07/17 03:04:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "executor/executor.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecScan
|
||||
|
@ -3,36 +3,38 @@
|
||||
* nodeAgg.c
|
||||
* Routines to handle aggregate nodes.
|
||||
*
|
||||
* ExecAgg evaluates each aggregate in the following steps: (initcond1,
|
||||
* initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are
|
||||
* the transition functions.)
|
||||
* ExecAgg evaluates each aggregate in the following steps:
|
||||
*
|
||||
* value1 = initcond1
|
||||
* value2 = initcond2
|
||||
* transvalue = initcond
|
||||
* foreach input_value do
|
||||
* value1 = sfunc1(value1, input_value)
|
||||
* value2 = sfunc2(value2)
|
||||
* value1 = finalfunc(value1, value2)
|
||||
* transvalue = transfunc(transvalue, input_value)
|
||||
* result = finalfunc(transvalue)
|
||||
*
|
||||
* If initcond1 is NULL then the first non-NULL input_value is
|
||||
* assigned directly to value1. sfunc1 isn't applied until value1
|
||||
* is non-NULL.
|
||||
* If a finalfunc is not supplied then the result is just the ending
|
||||
* value of transvalue.
|
||||
*
|
||||
* sfunc1 is never applied when the current tuple's input_value is NULL.
|
||||
* sfunc2 is applied for each tuple if the aggref is marked 'usenulls',
|
||||
* otherwise it is only applied when input_value is not NULL.
|
||||
* (usenulls was formerly used for COUNT(*), but is no longer needed for
|
||||
* that purpose; as of 10/1999 the support for usenulls is dead code.
|
||||
* I have not removed it because it seems like a potentially useful
|
||||
* feature for user-defined aggregates. We'd just need to add a
|
||||
* flag column to pg_aggregate and a parameter to CREATE AGGREGATE...)
|
||||
* If transfunc is marked "strict" in pg_proc and initcond is NULL,
|
||||
* then the first non-NULL input_value is assigned directly to transvalue,
|
||||
* and transfunc isn't applied until the second non-NULL input_value.
|
||||
* The agg's input type and transtype must be the same in this case!
|
||||
*
|
||||
* If transfunc is marked "strict" then NULL input_values are skipped,
|
||||
* keeping the previous transvalue. If transfunc is not strict then it
|
||||
* is called for every input tuple and must deal with NULL initcond
|
||||
* or NULL input_value for itself.
|
||||
*
|
||||
* If finalfunc is marked "strict" then it is not called when the
|
||||
* ending transvalue is NULL, instead a NULL result is created
|
||||
* automatically (this is just the usual handling of strict functions,
|
||||
* of course). A non-strict finalfunc can make its own choice of
|
||||
* what to return for a NULL ending transvalue.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.69 2000/07/12 02:37:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.70 2000/07/17 03:04:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -45,6 +47,7 @@
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeAgg.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_oper.h"
|
||||
#include "parser/parse_type.h"
|
||||
@ -67,16 +70,15 @@ typedef struct AggStatePerAggData
|
||||
Aggref *aggref;
|
||||
|
||||
/* Oids of transfer functions */
|
||||
Oid xfn1_oid;
|
||||
Oid xfn2_oid;
|
||||
Oid finalfn_oid;
|
||||
Oid transfn_oid;
|
||||
Oid finalfn_oid; /* may be InvalidOid */
|
||||
|
||||
/*
|
||||
* fmgr lookup data for transfer functions --- only valid when
|
||||
* corresponding oid is not InvalidOid
|
||||
* corresponding oid is not InvalidOid. Note in particular that
|
||||
* fn_strict flags are kept here.
|
||||
*/
|
||||
FmgrInfo xfn1;
|
||||
FmgrInfo xfn2;
|
||||
FmgrInfo transfn;
|
||||
FmgrInfo finalfn;
|
||||
|
||||
/*
|
||||
@ -94,12 +96,10 @@ typedef struct AggStatePerAggData
|
||||
FmgrInfo equalfn;
|
||||
|
||||
/*
|
||||
* initial values from pg_aggregate entry
|
||||
* initial value from pg_aggregate entry
|
||||
*/
|
||||
Datum initValue1; /* for transtype1 */
|
||||
Datum initValue2; /* for transtype2 */
|
||||
bool initValue1IsNull,
|
||||
initValue2IsNull;
|
||||
Datum initValue;
|
||||
bool initValueIsNull;
|
||||
|
||||
/*
|
||||
* We need the len and byval info for the agg's input, result, and
|
||||
@ -107,45 +107,42 @@ typedef struct AggStatePerAggData
|
||||
*/
|
||||
int inputtypeLen,
|
||||
resulttypeLen,
|
||||
transtype1Len,
|
||||
transtype2Len;
|
||||
transtypeLen;
|
||||
bool inputtypeByVal,
|
||||
resulttypeByVal,
|
||||
transtype1ByVal,
|
||||
transtype2ByVal;
|
||||
transtypeByVal;
|
||||
|
||||
/*
|
||||
* These values are working state that is initialized at the start of
|
||||
* an input tuple group and updated for each input tuple.
|
||||
*
|
||||
* For a simple (non DISTINCT) aggregate, we just feed the input values
|
||||
* straight to the transition functions. If it's DISTINCT, we pass
|
||||
* straight to the transition function. If it's DISTINCT, we pass
|
||||
* the input values into a Tuplesort object; then at completion of the
|
||||
* input tuple group, we scan the sorted values, eliminate duplicates,
|
||||
* and run the transition functions on the rest.
|
||||
* and run the transition function on the rest.
|
||||
*/
|
||||
|
||||
Tuplesortstate *sortstate; /* sort object, if a DISTINCT agg */
|
||||
|
||||
Datum value1, /* current transfer values 1 and 2 */
|
||||
value2;
|
||||
bool value1IsNull,
|
||||
value2IsNull;
|
||||
bool noInitValue; /* true if value1 not set yet */
|
||||
Datum transValue;
|
||||
bool transValueIsNull;
|
||||
|
||||
bool noTransValue; /* true if transValue not set yet */
|
||||
|
||||
/*
|
||||
* Note: right now, noInitValue always has the same value as
|
||||
* value1IsNull. But we should keep them separate because once the
|
||||
* fmgr interface is fixed, we'll need to distinguish a null returned
|
||||
* by transfn1 from a null we haven't yet replaced with an input
|
||||
* value.
|
||||
* Note: noTransValue initially has the same value as transValueIsNull,
|
||||
* and if true both are cleared to false at the same time. They are
|
||||
* not the same though: if transfn later returns a NULL, we want to
|
||||
* keep that NULL and not auto-replace it with a later input value.
|
||||
* Only the first non-NULL input will be auto-substituted.
|
||||
*/
|
||||
} AggStatePerAggData;
|
||||
|
||||
|
||||
static void initialize_aggregate(AggStatePerAgg peraggstate);
|
||||
static void advance_transition_functions(AggStatePerAgg peraggstate,
|
||||
Datum newVal, bool isNull);
|
||||
static void advance_transition_function(AggStatePerAgg peraggstate,
|
||||
Datum newVal, bool isNull);
|
||||
static void process_sorted_aggregate(AggState *aggstate,
|
||||
AggStatePerAgg peraggstate);
|
||||
static void finalize_aggregate(AggStatePerAgg peraggstate,
|
||||
@ -182,144 +179,118 @@ initialize_aggregate(AggStatePerAgg peraggstate)
|
||||
}
|
||||
|
||||
/*
|
||||
* (Re)set value1 and value2 to their initial values.
|
||||
* (Re)set transValue to the initial value.
|
||||
*
|
||||
* Note that when the initial values are pass-by-ref, we just reuse
|
||||
* them without copying for each group. Hence, transition function
|
||||
* had better not scribble on its input!
|
||||
* Note that when the initial value is pass-by-ref, we just reuse it
|
||||
* without copying for each group. Hence, transition function
|
||||
* had better not scribble on its input, or it will fail for GROUP BY!
|
||||
*/
|
||||
peraggstate->value1 = peraggstate->initValue1;
|
||||
peraggstate->value1IsNull = peraggstate->initValue1IsNull;
|
||||
peraggstate->value2 = peraggstate->initValue2;
|
||||
peraggstate->value2IsNull = peraggstate->initValue2IsNull;
|
||||
peraggstate->transValue = peraggstate->initValue;
|
||||
peraggstate->transValueIsNull = peraggstate->initValueIsNull;
|
||||
|
||||
/* ------------------------------------------
|
||||
* If the initial value for the first transition function
|
||||
* doesn't exist in the pg_aggregate table then we will let
|
||||
* the first value returned from the outer procNode become
|
||||
* the initial value. (This is useful for aggregates like
|
||||
* max{} and min{}.) The noInitValue flag signals that we
|
||||
* still need to do this.
|
||||
* If the initial value for the transition state doesn't exist in the
|
||||
* pg_aggregate table then we will let the first non-NULL value returned
|
||||
* from the outer procNode become the initial value. (This is useful for
|
||||
* aggregates like max() and min().) The noTransValue flag signals that
|
||||
* we still need to do this.
|
||||
* ------------------------------------------
|
||||
*/
|
||||
peraggstate->noInitValue = peraggstate->initValue1IsNull;
|
||||
peraggstate->noTransValue = peraggstate->initValueIsNull;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a new input value, advance the transition functions of an aggregate.
|
||||
* Given a new input value, advance the transition function of an aggregate.
|
||||
*
|
||||
* When called, CurrentMemoryContext should be the context we want transition
|
||||
* function results to be delivered into on this cycle.
|
||||
*
|
||||
* Note: if the agg does not have usenulls set, null inputs will be filtered
|
||||
* out before reaching here.
|
||||
* When called, CurrentMemoryContext should be the context we want the
|
||||
* transition function result to be delivered into on this cycle.
|
||||
*/
|
||||
static void
|
||||
advance_transition_functions(AggStatePerAgg peraggstate,
|
||||
Datum newVal, bool isNull)
|
||||
advance_transition_function(AggStatePerAgg peraggstate,
|
||||
Datum newVal, bool isNull)
|
||||
{
|
||||
FunctionCallInfoData fcinfo;
|
||||
|
||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||
|
||||
/*
|
||||
* XXX reconsider isNULL handling here
|
||||
*/
|
||||
if (OidIsValid(peraggstate->xfn1_oid) && !isNull)
|
||||
if (peraggstate->transfn.fn_strict)
|
||||
{
|
||||
if (peraggstate->noInitValue)
|
||||
if (isNull)
|
||||
{
|
||||
|
||||
/*
|
||||
* value1 has not been initialized. This is the first non-NULL
|
||||
* input value. We use it as the initial value for value1.
|
||||
*
|
||||
* XXX We assume, without having checked, that the agg's input
|
||||
* type is binary-compatible with its transtype1!
|
||||
* For a strict transfn, nothing happens at a NULL input tuple;
|
||||
* we just keep the prior transValue. However, if the transtype
|
||||
* is pass-by-ref, we have to copy it into the new context
|
||||
* because the old one is going to get reset.
|
||||
*/
|
||||
if (!peraggstate->transValueIsNull)
|
||||
peraggstate->transValue = datumCopy(peraggstate->transValue,
|
||||
peraggstate->transtypeByVal,
|
||||
peraggstate->transtypeLen);
|
||||
return;
|
||||
}
|
||||
if (peraggstate->noTransValue)
|
||||
{
|
||||
/*
|
||||
* transValue has not been initialized. This is the first non-NULL
|
||||
* input value. We use it as the initial value for transValue.
|
||||
* (We already checked that the agg's input type is binary-
|
||||
* compatible with its transtype, so straight copy here is OK.)
|
||||
*
|
||||
* We had better copy the datum if it is pass-by-ref, since
|
||||
* the given pointer may be pointing into a scan tuple that
|
||||
* will be freed on the next iteration of the scan.
|
||||
*/
|
||||
peraggstate->value1 = datumCopy(newVal,
|
||||
peraggstate->transtype1ByVal,
|
||||
peraggstate->transtype1Len);
|
||||
peraggstate->value1IsNull = false;
|
||||
peraggstate->noInitValue = false;
|
||||
peraggstate->transValue = datumCopy(newVal,
|
||||
peraggstate->transtypeByVal,
|
||||
peraggstate->transtypeLen);
|
||||
peraggstate->transValueIsNull = false;
|
||||
peraggstate->noTransValue = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
if (peraggstate->transValueIsNull)
|
||||
{
|
||||
/* apply transition function 1 */
|
||||
fcinfo.flinfo = &peraggstate->xfn1;
|
||||
fcinfo.nargs = 2;
|
||||
fcinfo.arg[0] = peraggstate->value1;
|
||||
fcinfo.argnull[0] = peraggstate->value1IsNull;
|
||||
fcinfo.arg[1] = newVal;
|
||||
fcinfo.argnull[1] = isNull;
|
||||
if (fcinfo.flinfo->fn_strict &&
|
||||
(peraggstate->value1IsNull || isNull))
|
||||
{
|
||||
/* don't call a strict function with NULL inputs */
|
||||
newVal = (Datum) 0;
|
||||
fcinfo.isnull = true;
|
||||
}
|
||||
else
|
||||
newVal = FunctionCallInvoke(&fcinfo);
|
||||
/*
|
||||
* If the transition function was uncooperative, it may have
|
||||
* given us a pass-by-ref result that points at the scan tuple
|
||||
* or the prior-cycle working memory. Copy it into the active
|
||||
* context if it doesn't look right.
|
||||
* Don't call a strict function with NULL inputs. Note it is
|
||||
* possible to get here despite the above tests, if the transfn
|
||||
* is strict *and* returned a NULL on a prior cycle. If that
|
||||
* happens we will propagate the NULL all the way to the end.
|
||||
*/
|
||||
if (!peraggstate->transtype1ByVal && !fcinfo.isnull &&
|
||||
! MemoryContextContains(CurrentMemoryContext,
|
||||
DatumGetPointer(newVal)))
|
||||
newVal = datumCopy(newVal,
|
||||
peraggstate->transtype1ByVal,
|
||||
peraggstate->transtype1Len);
|
||||
peraggstate->value1 = newVal;
|
||||
peraggstate->value1IsNull = fcinfo.isnull;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (OidIsValid(peraggstate->xfn2_oid))
|
||||
{
|
||||
/* apply transition function 2 */
|
||||
fcinfo.flinfo = &peraggstate->xfn2;
|
||||
fcinfo.nargs = 1;
|
||||
fcinfo.arg[0] = peraggstate->value2;
|
||||
fcinfo.argnull[0] = peraggstate->value2IsNull;
|
||||
fcinfo.isnull = false; /* must reset after use by xfn1 */
|
||||
if (fcinfo.flinfo->fn_strict && peraggstate->value2IsNull)
|
||||
{
|
||||
/* don't call a strict function with NULL inputs */
|
||||
newVal = (Datum) 0;
|
||||
fcinfo.isnull = true;
|
||||
}
|
||||
else
|
||||
newVal = FunctionCallInvoke(&fcinfo);
|
||||
/*
|
||||
* If the transition function was uncooperative, it may have
|
||||
* given us a pass-by-ref result that points at the scan tuple
|
||||
* or the prior-cycle working memory. Copy it into the active
|
||||
* context if it doesn't look right.
|
||||
*/
|
||||
if (!peraggstate->transtype2ByVal && !fcinfo.isnull &&
|
||||
! MemoryContextContains(CurrentMemoryContext,
|
||||
DatumGetPointer(newVal)))
|
||||
newVal = datumCopy(newVal,
|
||||
peraggstate->transtype2ByVal,
|
||||
peraggstate->transtype2Len);
|
||||
peraggstate->value2 = newVal;
|
||||
peraggstate->value2IsNull = fcinfo.isnull;
|
||||
}
|
||||
/* OK to call the transition function */
|
||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||
fcinfo.flinfo = &peraggstate->transfn;
|
||||
fcinfo.nargs = 2;
|
||||
fcinfo.arg[0] = peraggstate->transValue;
|
||||
fcinfo.argnull[0] = peraggstate->transValueIsNull;
|
||||
fcinfo.arg[1] = newVal;
|
||||
fcinfo.argnull[1] = isNull;
|
||||
|
||||
newVal = FunctionCallInvoke(&fcinfo);
|
||||
|
||||
/*
|
||||
* If the transition function was uncooperative, it may have
|
||||
* given us a pass-by-ref result that points at the scan tuple
|
||||
* or the prior-cycle working memory. Copy it into the active
|
||||
* context if it doesn't look right.
|
||||
*/
|
||||
if (!peraggstate->transtypeByVal && !fcinfo.isnull &&
|
||||
! MemoryContextContains(CurrentMemoryContext,
|
||||
DatumGetPointer(newVal)))
|
||||
newVal = datumCopy(newVal,
|
||||
peraggstate->transtypeByVal,
|
||||
peraggstate->transtypeLen);
|
||||
|
||||
peraggstate->transValue = newVal;
|
||||
peraggstate->transValueIsNull = fcinfo.isnull;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the transition functions for a DISTINCT aggregate. This is called
|
||||
* Run the transition function for a DISTINCT aggregate. This is called
|
||||
* after we have completed entering all the input values into the sort
|
||||
* object. We complete the sort, read out the value in sorted order, and
|
||||
* run the transition functions on each non-duplicate value.
|
||||
* object. We complete the sort, read out the values in sorted order,
|
||||
* and run the transition function on each non-duplicate value.
|
||||
*
|
||||
* When called, CurrentMemoryContext should be the per-query context.
|
||||
*/
|
||||
@ -346,13 +317,13 @@ process_sorted_aggregate(AggState *aggstate,
|
||||
{
|
||||
/*
|
||||
* DISTINCT always suppresses nulls, per SQL spec, regardless of
|
||||
* the aggregate's usenulls setting.
|
||||
* the transition function's strictness.
|
||||
*/
|
||||
if (isNull)
|
||||
continue;
|
||||
/*
|
||||
* Clear and select the current working context for evaluation of
|
||||
* the equality function and transition functions.
|
||||
* the equality function and transition function.
|
||||
*/
|
||||
MemoryContextReset(aggstate->agg_cxt[aggstate->which_cxt]);
|
||||
oldContext =
|
||||
@ -365,11 +336,14 @@ process_sorted_aggregate(AggState *aggstate,
|
||||
/* equal to prior, so forget this one */
|
||||
if (!peraggstate->inputtypeByVal)
|
||||
pfree(DatumGetPointer(newVal));
|
||||
/* note we do NOT flip contexts in this case... */
|
||||
/*
|
||||
* note we do NOT flip contexts in this case, so no need to
|
||||
* copy prior transValue to other context.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
advance_transition_functions(peraggstate, newVal, false);
|
||||
advance_transition_function(peraggstate, newVal, false);
|
||||
/*
|
||||
* Make the other context current so that this transition
|
||||
* result is preserved.
|
||||
@ -402,48 +376,19 @@ static void
|
||||
finalize_aggregate(AggStatePerAgg peraggstate,
|
||||
Datum *resultVal, bool *resultIsNull)
|
||||
{
|
||||
FunctionCallInfoData fcinfo;
|
||||
|
||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||
|
||||
/*
|
||||
* Apply the agg's finalfn, or substitute the appropriate
|
||||
* transition value if there is no finalfn.
|
||||
*
|
||||
* XXX For now, only apply finalfn if we got at least one non-null input
|
||||
* value. This prevents zero divide in AVG(). If we had cleaner
|
||||
* handling of null inputs/results in functions, we could probably
|
||||
* take out this hack and define the result for no inputs as whatever
|
||||
* finalfn returns for null input.
|
||||
* Apply the agg's finalfn if one is provided, else return transValue.
|
||||
*/
|
||||
if (OidIsValid(peraggstate->finalfn_oid) &&
|
||||
!peraggstate->noInitValue)
|
||||
if (OidIsValid(peraggstate->finalfn_oid))
|
||||
{
|
||||
FunctionCallInfoData fcinfo;
|
||||
|
||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||
fcinfo.flinfo = &peraggstate->finalfn;
|
||||
if (peraggstate->finalfn.fn_nargs > 1)
|
||||
{
|
||||
fcinfo.nargs = 2;
|
||||
fcinfo.arg[0] = peraggstate->value1;
|
||||
fcinfo.argnull[0] = peraggstate->value1IsNull;
|
||||
fcinfo.arg[1] = peraggstate->value2;
|
||||
fcinfo.argnull[1] = peraggstate->value2IsNull;
|
||||
}
|
||||
else if (OidIsValid(peraggstate->xfn1_oid))
|
||||
{
|
||||
fcinfo.nargs = 1;
|
||||
fcinfo.arg[0] = peraggstate->value1;
|
||||
fcinfo.argnull[0] = peraggstate->value1IsNull;
|
||||
}
|
||||
else if (OidIsValid(peraggstate->xfn2_oid))
|
||||
{
|
||||
fcinfo.nargs = 1;
|
||||
fcinfo.arg[0] = peraggstate->value2;
|
||||
fcinfo.argnull[0] = peraggstate->value2IsNull;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "ExecAgg: no valid transition functions??");
|
||||
if (fcinfo.flinfo->fn_strict &&
|
||||
(fcinfo.argnull[0] || fcinfo.argnull[1]))
|
||||
fcinfo.nargs = 1;
|
||||
fcinfo.arg[0] = peraggstate->transValue;
|
||||
fcinfo.argnull[0] = peraggstate->transValueIsNull;
|
||||
if (fcinfo.flinfo->fn_strict && peraggstate->transValueIsNull)
|
||||
{
|
||||
/* don't call a strict function with NULL inputs */
|
||||
*resultVal = (Datum) 0;
|
||||
@ -455,20 +400,12 @@ finalize_aggregate(AggStatePerAgg peraggstate,
|
||||
*resultIsNull = fcinfo.isnull;
|
||||
}
|
||||
}
|
||||
else if (OidIsValid(peraggstate->xfn1_oid))
|
||||
{
|
||||
/* Return value1 */
|
||||
*resultVal = peraggstate->value1;
|
||||
*resultIsNull = peraggstate->value1IsNull;
|
||||
}
|
||||
else if (OidIsValid(peraggstate->xfn2_oid))
|
||||
{
|
||||
/* Return value2 */
|
||||
*resultVal = peraggstate->value2;
|
||||
*resultIsNull = peraggstate->value2IsNull;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "ExecAgg: no valid transition functions??");
|
||||
{
|
||||
*resultVal = peraggstate->transValue;
|
||||
*resultIsNull = peraggstate->transValueIsNull;
|
||||
}
|
||||
|
||||
/*
|
||||
* If result is pass-by-ref, make sure it is in the right context.
|
||||
*/
|
||||
@ -588,11 +525,11 @@ ExecAgg(Agg *node)
|
||||
newVal = ExecEvalExpr(aggref->target, econtext,
|
||||
&isNull, &isDone);
|
||||
|
||||
if (isNull && !aggref->usenulls)
|
||||
continue; /* ignore this tuple for this agg */
|
||||
|
||||
if (aggref->aggdistinct)
|
||||
{
|
||||
/* in DISTINCT mode, we may ignore nulls */
|
||||
if (isNull)
|
||||
continue;
|
||||
/* putdatum has to be called in per-query context */
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
tuplesort_putdatum(peraggstate->sortstate,
|
||||
@ -600,8 +537,10 @@ ExecAgg(Agg *node)
|
||||
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||
}
|
||||
else
|
||||
advance_transition_functions(peraggstate,
|
||||
newVal, isNull);
|
||||
{
|
||||
advance_transition_function(peraggstate,
|
||||
newVal, isNull);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -889,8 +828,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
|
||||
HeapTuple aggTuple;
|
||||
Form_pg_aggregate aggform;
|
||||
Type typeInfo;
|
||||
Oid xfn1_oid,
|
||||
xfn2_oid,
|
||||
Oid transfn_oid,
|
||||
finalfn_oid;
|
||||
|
||||
/* Mark Aggref node with its associated index in the result array */
|
||||
@ -913,53 +851,51 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
|
||||
peraggstate->resulttypeLen = typeLen(typeInfo);
|
||||
peraggstate->resulttypeByVal = typeByVal(typeInfo);
|
||||
|
||||
peraggstate->initValue1 =
|
||||
typeInfo = typeidType(aggform->aggtranstype);
|
||||
peraggstate->transtypeLen = typeLen(typeInfo);
|
||||
peraggstate->transtypeByVal = typeByVal(typeInfo);
|
||||
|
||||
peraggstate->initValue =
|
||||
AggNameGetInitVal(aggname,
|
||||
aggform->aggbasetype,
|
||||
1,
|
||||
&peraggstate->initValue1IsNull);
|
||||
&peraggstate->initValueIsNull);
|
||||
|
||||
peraggstate->initValue2 =
|
||||
AggNameGetInitVal(aggname,
|
||||
aggform->aggbasetype,
|
||||
2,
|
||||
&peraggstate->initValue2IsNull);
|
||||
|
||||
peraggstate->xfn1_oid = xfn1_oid = aggform->aggtransfn1;
|
||||
peraggstate->xfn2_oid = xfn2_oid = aggform->aggtransfn2;
|
||||
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
|
||||
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
|
||||
|
||||
if (OidIsValid(xfn1_oid))
|
||||
{
|
||||
fmgr_info(xfn1_oid, &peraggstate->xfn1);
|
||||
/* If a transfn1 is specified, transtype1 had better be, too */
|
||||
typeInfo = typeidType(aggform->aggtranstype1);
|
||||
peraggstate->transtype1Len = typeLen(typeInfo);
|
||||
peraggstate->transtype1ByVal = typeByVal(typeInfo);
|
||||
}
|
||||
|
||||
if (OidIsValid(xfn2_oid))
|
||||
{
|
||||
fmgr_info(xfn2_oid, &peraggstate->xfn2);
|
||||
/* If a transfn2 is specified, transtype2 had better be, too */
|
||||
typeInfo = typeidType(aggform->aggtranstype2);
|
||||
peraggstate->transtype2Len = typeLen(typeInfo);
|
||||
peraggstate->transtype2ByVal = typeByVal(typeInfo);
|
||||
/* ------------------------------------------
|
||||
* If there is a second transition function, its initial
|
||||
* value must exist -- as it does not depend on data values,
|
||||
* we have no other way of determining an initial value.
|
||||
* ------------------------------------------
|
||||
*/
|
||||
if (peraggstate->initValue2IsNull)
|
||||
elog(ERROR, "ExecInitAgg: agginitval2 is null");
|
||||
}
|
||||
|
||||
fmgr_info(transfn_oid, &peraggstate->transfn);
|
||||
if (OidIsValid(finalfn_oid))
|
||||
fmgr_info(finalfn_oid, &peraggstate->finalfn);
|
||||
|
||||
/*
|
||||
* If the transfn is strict and the initval is NULL, make sure
|
||||
* input type and transtype are the same (or at least binary-
|
||||
* compatible), so that it's OK to use the first input value
|
||||
* as the initial transValue. This should have been checked at
|
||||
* agg definition time, but just in case...
|
||||
*/
|
||||
if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
|
||||
{
|
||||
/*
|
||||
* Note: use the type from the input expression here,
|
||||
* not aggform->aggbasetype, because the latter might be 0.
|
||||
* (Consider COUNT(*).)
|
||||
*/
|
||||
Oid inputType = exprType(aggref->target);
|
||||
|
||||
if (inputType != aggform->aggtranstype &&
|
||||
! IS_BINARY_COMPATIBLE(inputType, aggform->aggtranstype))
|
||||
elog(ERROR, "Aggregate %s needs to have compatible input type and transition type",
|
||||
aggname);
|
||||
}
|
||||
|
||||
if (aggref->aggdistinct)
|
||||
{
|
||||
/*
|
||||
* Note: use the type from the input expression here,
|
||||
* not aggform->aggbasetype, because the latter might be 0.
|
||||
* (Consider COUNT(*).)
|
||||
*/
|
||||
Oid inputType = exprType(aggref->target);
|
||||
Operator eq_operator;
|
||||
Form_pg_operator pgopform;
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* $Id: nodeHash.c,v 1.49 2000/07/12 02:37:03 tgl Exp $
|
||||
* $Id: nodeHash.c,v 1.50 2000/07/17 03:04:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -30,6 +30,8 @@
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
static int hashFunc(Datum key, int len, bool byVal);
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.31 2000/07/12 02:37:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.32 2000/07/17 03:04:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -20,6 +20,8 @@
|
||||
#include "executor/nodeHash.h"
|
||||
#include "executor/nodeHashjoin.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
static TupleTableSlot *ExecHashJoinOuterGetTuple(Plan *node, Plan *parent,
|
||||
HashJoinState *hjstate);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.17 2000/07/12 02:37:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.18 2000/07/17 03:04:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -18,10 +18,13 @@
|
||||
* ExecInitNestLoop - initialize the join
|
||||
* ExecEndNestLoop - shut down the join
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "executor/execdebug.h"
|
||||
#include "executor/nodeNestloop.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecNestLoop(node)
|
||||
|
@ -34,7 +34,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.14 2000/07/12 02:37:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.15 2000/07/17 03:04:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -43,6 +43,8 @@
|
||||
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeResult.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecResult(node)
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.49 2000/07/07 21:12:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.50 2000/07/17 03:04:54 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* This should be moved to a more appropriate place. It is here
|
||||
@ -43,6 +43,8 @@
|
||||
#include "libpq/be-fsstubs.h"
|
||||
#include "libpq/libpq-fs.h"
|
||||
#include "storage/large_object.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
/* [PA] is Pascal André <andre@via.ecp.fr> */
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.116 2000/07/12 02:37:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.117 2000/07/17 03:04:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -843,7 +843,6 @@ _copyAggref(Aggref *from)
|
||||
newnode->basetype = from->basetype;
|
||||
newnode->aggtype = from->aggtype;
|
||||
Node_Copy(from, newnode, target);
|
||||
newnode->usenulls = from->usenulls;
|
||||
newnode->aggstar = from->aggstar;
|
||||
newnode->aggdistinct = from->aggdistinct;
|
||||
newnode->aggno = from->aggno; /* probably not needed */
|
||||
|
@ -24,7 +24,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.68 2000/07/12 02:37:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.69 2000/07/17 03:05:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -257,8 +257,6 @@ _equalAggref(Aggref *a, Aggref *b)
|
||||
return false;
|
||||
if (!equal(a->target, b->target))
|
||||
return false;
|
||||
if (a->usenulls != b->usenulls)
|
||||
return false;
|
||||
if (a->aggstar != b->aggstar)
|
||||
return false;
|
||||
if (a->aggdistinct != b->aggdistinct)
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.122 2000/07/15 00:01:37 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.123 2000/07/17 03:05:01 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||
@ -729,12 +729,10 @@ _outAggref(StringInfo str, Aggref *node)
|
||||
appendStringInfo(str, " AGGREG :aggname ");
|
||||
_outToken(str, node->aggname);
|
||||
appendStringInfo(str, " :basetype %u :aggtype %u :target ",
|
||||
node->basetype,
|
||||
node->aggtype);
|
||||
node->basetype, node->aggtype);
|
||||
_outNode(str, node->target);
|
||||
|
||||
appendStringInfo(str, " :usenulls %s :aggstar %s :aggdistinct %s ",
|
||||
node->usenulls ? "true" : "false",
|
||||
appendStringInfo(str, " :aggstar %s :aggdistinct %s ",
|
||||
node->aggstar ? "true" : "false",
|
||||
node->aggdistinct ? "true" : "false");
|
||||
/* aggno is not dumped */
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.92 2000/07/12 02:37:06 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.93 2000/07/17 03:05:01 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||
@ -1117,10 +1117,6 @@ _readAggref()
|
||||
token = lsptok(NULL, &length); /* eat :target */
|
||||
local_node->target = nodeRead(true); /* now read it */
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :usenulls */
|
||||
token = lsptok(NULL, &length); /* get usenulls */
|
||||
local_node->usenulls = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :aggstar */
|
||||
token = lsptok(NULL, &length); /* get aggstar */
|
||||
local_node->aggstar = (token[0] == 't') ? true : false;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.38 2000/06/15 03:32:19 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.39 2000/07/17 03:05:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -189,18 +189,16 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
|
||||
{
|
||||
HeapTuple theAggTuple;
|
||||
Form_pg_aggregate aggform;
|
||||
Oid fintype;
|
||||
Oid xfn1;
|
||||
Oid vartype;
|
||||
Aggref *aggref;
|
||||
bool usenulls = false;
|
||||
|
||||
theAggTuple = SearchSysCacheTuple(AGGNAME,
|
||||
PointerGetDatum(aggname),
|
||||
ObjectIdGetDatum(basetype),
|
||||
0, 0);
|
||||
/* shouldn't happen --- caller should have checked already */
|
||||
if (!HeapTupleIsValid(theAggTuple))
|
||||
elog(ERROR, "Aggregate %s does not exist", aggname);
|
||||
agg_error("ParseAgg", aggname, basetype);
|
||||
aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
|
||||
|
||||
/*
|
||||
* There used to be a really ugly hack for count(*) here.
|
||||
@ -209,43 +207,18 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
|
||||
* does the right thing. (It didn't use to do the right thing,
|
||||
* because the optimizer had the wrong ideas about semantics of
|
||||
* queries without explicit variables. Fixed as of Oct 1999 --- tgl.)
|
||||
*
|
||||
* Since "1" never evaluates as null, we currently have no need of the
|
||||
* "usenulls" flag, but it should be kept around; in fact, we should
|
||||
* extend the pg_aggregate table to let usenulls be specified as an
|
||||
* attribute of user-defined aggregates. In the meantime, usenulls is
|
||||
* just always set to "false".
|
||||
*/
|
||||
|
||||
aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
|
||||
fintype = aggform->aggfinaltype;
|
||||
xfn1 = aggform->aggtransfn1;
|
||||
|
||||
/* only aggregates with transfn1 need a base type */
|
||||
if (OidIsValid(xfn1))
|
||||
{
|
||||
basetype = aggform->aggbasetype;
|
||||
vartype = exprType(lfirst(args));
|
||||
if ((basetype != vartype)
|
||||
&& (!IS_BINARY_COMPATIBLE(basetype, vartype)))
|
||||
{
|
||||
Type tp1,
|
||||
tp2;
|
||||
|
||||
tp1 = typeidType(basetype);
|
||||
tp2 = typeidType(vartype);
|
||||
elog(ERROR, "Aggregate type mismatch"
|
||||
"\n\t%s() works on %s, not on %s",
|
||||
aggname, typeTypeName(tp1), typeTypeName(tp2));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We assume caller has already checked that given args are compatible
|
||||
* with the agg's basetype.
|
||||
*/
|
||||
|
||||
aggref = makeNode(Aggref);
|
||||
aggref->aggname = pstrdup(aggname);
|
||||
aggref->basetype = aggform->aggbasetype;
|
||||
aggref->aggtype = fintype;
|
||||
aggref->aggtype = aggform->aggfinaltype;
|
||||
aggref->target = lfirst(args);
|
||||
aggref->usenulls = usenulls;
|
||||
aggref->aggstar = agg_star;
|
||||
aggref->aggdistinct = agg_distinct;
|
||||
|
||||
@ -268,10 +241,9 @@ agg_error(char *caller, char *aggname, Oid basetypeID)
|
||||
*/
|
||||
|
||||
if (basetypeID == InvalidOid)
|
||||
elog(ERROR, "%s: aggregate '%s' for all types does not exist", caller, aggname);
|
||||
elog(ERROR, "%s: aggregate '%s' for all types does not exist",
|
||||
caller, aggname);
|
||||
else
|
||||
{
|
||||
elog(ERROR, "%s: aggregate '%s' for '%s' does not exist", caller, aggname,
|
||||
typeidTypeName(basetypeID));
|
||||
}
|
||||
elog(ERROR, "%s: aggregate '%s' for '%s' does not exist",
|
||||
caller, aggname, typeidTypeName(basetypeID));
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.156 2000/07/12 22:59:04 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.157 2000/07/17 03:05:04 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
@ -33,6 +33,7 @@
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include <unistd.h>
|
||||
@ -80,6 +81,7 @@
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "utils/exc.h"
|
||||
#include "utils/guc.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
#define INVALID_SOCK (-1)
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.70 2000/06/28 03:32:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.71 2000/07/17 03:05:08 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Outside modules can create a lock table and acquire/release
|
||||
@ -20,7 +20,7 @@
|
||||
* Interface:
|
||||
*
|
||||
* LockAcquire(), LockRelease(), LockMethodTableInit(),
|
||||
* LockMethodTableRename(), LockReleaseAll, LockOwners()
|
||||
* LockMethodTableRename(), LockReleaseAll,
|
||||
* LockResolveConflicts(), GrantLock()
|
||||
*
|
||||
* NOTE: This module is used to define new lock tables. The
|
||||
@ -35,9 +35,11 @@
|
||||
#include <signal.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/xact.h"
|
||||
#include "miscadmin.h"
|
||||
#include "storage/proc.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/ps_status.h"
|
||||
|
||||
static int WaitOnLock(LOCKMETHOD lockmethod, LOCK *lock, LOCKMODE lockmode);
|
||||
@ -1722,181 +1724,6 @@ nxtl: ;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
/*
|
||||
* Return an array with the pids of all processes owning a lock.
|
||||
* This works only for user locks because normal locks have no
|
||||
* pid information in the corresponding XIDLookupEnt.
|
||||
*/
|
||||
ArrayType *
|
||||
LockOwners(LOCKMETHOD lockmethod, LOCKTAG *locktag)
|
||||
{
|
||||
XIDLookupEnt *xidLook = NULL;
|
||||
SPINLOCK masterLock;
|
||||
LOCK *lock;
|
||||
SHMEM_OFFSET lock_offset;
|
||||
int count = 0;
|
||||
LOCKMETHODTABLE *lockMethodTable;
|
||||
HTAB *xidTable;
|
||||
bool found;
|
||||
int ndims,
|
||||
nitems,
|
||||
hdrlen,
|
||||
size;
|
||||
int lbounds[1],
|
||||
hbounds[1];
|
||||
ArrayType *array;
|
||||
int *data_ptr;
|
||||
|
||||
/* Assume that no one will modify the result */
|
||||
static int empty_array[] = {20, 1, 0, 0, 0};
|
||||
|
||||
#ifdef LOCK_DEBUG
|
||||
if (lockmethod == USER_LOCKMETHOD && Trace_userlocks)
|
||||
elog(DEBUG, "LockOwners: user lock tag [%u]", locktag->objId.blkno);
|
||||
#endif
|
||||
|
||||
/* This must be changed when short term locks will be used */
|
||||
locktag->lockmethod = lockmethod;
|
||||
|
||||
Assert((lockmethod >= MIN_LOCKMETHOD) && (lockmethod < NumLockMethods));
|
||||
lockMethodTable = LockMethodTable[lockmethod];
|
||||
if (!lockMethodTable)
|
||||
{
|
||||
elog(NOTICE, "lockMethodTable is null in LockOwners");
|
||||
return (ArrayType *) &empty_array;
|
||||
}
|
||||
|
||||
if (LockingIsDisabled)
|
||||
return (ArrayType *) &empty_array;
|
||||
|
||||
masterLock = lockMethodTable->ctl->masterLock;
|
||||
SpinAcquire(masterLock);
|
||||
|
||||
/*
|
||||
* Find a lock with this tag
|
||||
*/
|
||||
Assert(lockMethodTable->lockHash->hash == tag_hash);
|
||||
lock = (LOCK *) hash_search(lockMethodTable->lockHash, (Pointer) locktag,
|
||||
HASH_FIND, &found);
|
||||
|
||||
/*
|
||||
* let the caller print its own error message, too. Do not elog(WARN).
|
||||
*/
|
||||
if (!lock)
|
||||
{
|
||||
SpinRelease(masterLock);
|
||||
elog(NOTICE, "LockOwners: locktable corrupted");
|
||||
return (ArrayType *) &empty_array;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
SpinRelease(masterLock);
|
||||
elog(NOTICE, "LockOwners: no such lock");
|
||||
return (ArrayType *) &empty_array;
|
||||
}
|
||||
LOCK_PRINT("LockOwners: found", lock, 0);
|
||||
Assert((lock->nHolding > 0) && (lock->nActive > 0));
|
||||
Assert(lock->nActive <= lock->nHolding);
|
||||
lock_offset = MAKE_OFFSET(lock);
|
||||
|
||||
/* Construct a 1-dimensional array */
|
||||
ndims = 1;
|
||||
hdrlen = ARR_OVERHEAD(ndims);
|
||||
lbounds[0] = 0;
|
||||
hbounds[0] = lock->nActive;
|
||||
size = hdrlen + sizeof(int) * hbounds[0];
|
||||
array = (ArrayType *) palloc(size);
|
||||
MemSet(array, 0, size);
|
||||
memmove((char *) array, (char *) &size, sizeof(int));
|
||||
memmove((char *) ARR_NDIM_PTR(array), (char *) &ndims, sizeof(int));
|
||||
memmove((char *) ARR_DIMS(array), (char *) hbounds, ndims * sizeof(int));
|
||||
memmove((char *) ARR_LBOUND(array), (char *) lbounds, ndims * sizeof(int));
|
||||
SET_LO_FLAG(false, array);
|
||||
data_ptr = (int *) ARR_DATA_PTR(array);
|
||||
|
||||
xidTable = lockMethodTable->xidHash;
|
||||
hash_seq(NULL);
|
||||
nitems = 0;
|
||||
while ((xidLook = (XIDLookupEnt *) hash_seq(xidTable)) &&
|
||||
(xidLook != (XIDLookupEnt *) TRUE))
|
||||
{
|
||||
if (count++ > 1000)
|
||||
{
|
||||
elog(NOTICE, "LockOwners: possible loop, giving up");
|
||||
break;
|
||||
}
|
||||
|
||||
if (xidLook->tag.pid == 0)
|
||||
{
|
||||
XID_PRINT("LockOwners: no pid", xidLook);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!xidLook->tag.lock)
|
||||
{
|
||||
XID_PRINT("LockOwners: NULL LOCK", xidLook);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (xidLook->tag.lock != lock_offset)
|
||||
{
|
||||
XID_PRINT("LockOwners: different lock", xidLook);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (LOCK_LOCKMETHOD(*lock) != lockmethod)
|
||||
{
|
||||
XID_PRINT("LockOwners: other table", xidLook);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (xidLook->nHolding <= 0)
|
||||
{
|
||||
XID_PRINT("LockOwners: not holding", xidLook);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nitems >= hbounds[0])
|
||||
{
|
||||
elog(NOTICE, "LockOwners: array size exceeded");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the holding process is still alive by sending him an
|
||||
* unused (ignored) signal. If the kill fails the process is not
|
||||
* alive.
|
||||
*/
|
||||
if ((xidLook->tag.pid != MyProcPid) \
|
||||
&&(kill(xidLook->tag.pid, SIGCHLD)) != 0)
|
||||
{
|
||||
/* Return a negative pid to signal that process is dead */
|
||||
data_ptr[nitems++] = -(xidLook->tag.pid);
|
||||
XID_PRINT("LockOwners: not alive", xidLook);
|
||||
/* XXX - TODO: remove this entry and update lock stats */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Found a process holding the lock */
|
||||
XID_PRINT("LockOwners: holding", xidLook);
|
||||
data_ptr[nitems++] = xidLook->tag.pid;
|
||||
}
|
||||
|
||||
SpinRelease(masterLock);
|
||||
|
||||
/* Adjust the actual size of the array */
|
||||
hbounds[0] = nitems;
|
||||
size = hdrlen + sizeof(int) * hbounds[0];
|
||||
memmove((char *) array, (char *) &size, sizeof(int));
|
||||
memmove((char *) ARR_DIMS(array), (char *) hbounds, ndims * sizeof(int));
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
#endif /* NOT_USED */
|
||||
|
||||
#ifdef LOCK_DEBUG
|
||||
/*
|
||||
* Dump all locks in the proc->lockQueue. Must have already acquired
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.73 2000/07/10 04:32:00 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.74 2000/07/17 03:05:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -22,8 +22,9 @@
|
||||
#include "catalog/catalog.h"
|
||||
#include "miscadmin.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "utils/inval.h" /* ImmediateSharedRelationCacheInvalidate()
|
||||
* */
|
||||
#include "utils/inval.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
#undef DIAGNOSTIC
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.169 2000/07/12 17:38:45 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.170 2000/07/17 03:05:14 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* this is the "main" module of the postgres backend and
|
||||
@ -56,6 +56,7 @@
|
||||
#include "storage/proc.h"
|
||||
#include "utils/exc.h"
|
||||
#include "utils/guc.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/ps_status.h"
|
||||
#include "utils/temprel.h"
|
||||
#ifdef MULTIBYTE
|
||||
@ -1411,7 +1412,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
||||
if (!IsUnderPostmaster)
|
||||
{
|
||||
puts("\nPOSTGRES backend interactive interface ");
|
||||
puts("$Revision: 1.169 $ $Date: 2000/07/12 17:38:45 $\n");
|
||||
puts("$Revision: 1.170 $ $Date: 2000/07/17 03:05:14 $\n");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.36 2000/07/12 02:37:15 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.37 2000/07/17 03:05:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -19,8 +19,10 @@
|
||||
#include "executor/execdefs.h"
|
||||
#include "executor/executor.h"
|
||||
#include "tcop/pquery.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/ps_status.h"
|
||||
|
||||
|
||||
static char *CreateOperationTag(int operationType);
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.60 2000/07/03 23:09:50 wieck Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.61 2000/07/17 03:05:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -19,7 +19,6 @@
|
||||
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "fmgr.h"
|
||||
#include "libpq/be-fsstubs.h"
|
||||
#include "libpq/libpq-fs.h"
|
||||
#include "storage/fd.h"
|
||||
@ -29,7 +28,8 @@
|
||||
|
||||
#define ASSGN "="
|
||||
|
||||
/* An array has the following internal structure:
|
||||
/*
|
||||
* An array has the following internal structure:
|
||||
* <nbytes> - total number of bytes
|
||||
* <ndim> - number of dimensions of the array
|
||||
* <flags> - bit mask of flags
|
||||
@ -38,20 +38,18 @@
|
||||
* <actual data> - whatever is the stored data
|
||||
*/
|
||||
|
||||
/*-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-*/
|
||||
static int _ArrayCount(char *str, int *dim, int typdelim);
|
||||
static char *_ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
|
||||
static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
|
||||
FmgrInfo *inputproc, Oid typelem, int32 typmod,
|
||||
char typdelim, int typlen, bool typbyval,
|
||||
char typalign, int *nbytes);
|
||||
|
||||
#ifdef LOARRAY
|
||||
static char *_ReadLOArray(char *str, int *nbytes, int *fd, bool *chunkFlag,
|
||||
int ndim, int *dim, int baseSize);
|
||||
|
||||
#endif
|
||||
static void _CopyArrayEls(char **values, char *p, int nitems, int typlen,
|
||||
char typalign, bool typbyval);
|
||||
static void CopyArrayEls(char *p, Datum *values, int nitems,
|
||||
bool typbyval, int typlen, char typalign,
|
||||
bool freedata);
|
||||
static void system_cache_lookup(Oid element_type, bool input, int *typlen,
|
||||
bool *typbyval, char *typdelim, Oid *typelem, Oid *proc,
|
||||
char *typalign);
|
||||
@ -101,7 +99,7 @@ array_in(PG_FUNCTION_ARGS)
|
||||
int i,
|
||||
nitems;
|
||||
int32 nbytes;
|
||||
char *dataPtr;
|
||||
Datum *dataPtr;
|
||||
ArrayType *retval;
|
||||
int ndim,
|
||||
dim[MAXDIM],
|
||||
@ -187,32 +185,29 @@ array_in(PG_FUNCTION_ARGS)
|
||||
retval = (ArrayType *) palloc(sizeof(ArrayType));
|
||||
MemSet(retval, 0, sizeof(ArrayType));
|
||||
*(int32 *) retval = sizeof(ArrayType);
|
||||
PG_RETURN_POINTER(retval);
|
||||
PG_RETURN_ARRAYTYPE_P(retval);
|
||||
}
|
||||
|
||||
if (*p == '{')
|
||||
{
|
||||
/* array not a large object */
|
||||
dataPtr = (char *) _ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem,
|
||||
typmod, typdelim, typlen, typbyval, typalign,
|
||||
&nbytes);
|
||||
dataPtr = ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem,
|
||||
typmod, typdelim, typlen, typbyval, typalign,
|
||||
&nbytes);
|
||||
nbytes += ARR_OVERHEAD(ndim);
|
||||
retval = (ArrayType *) palloc(nbytes);
|
||||
MemSet(retval, 0, nbytes);
|
||||
memmove(retval, (char *) &nbytes, sizeof(int));
|
||||
memmove((char *) ARR_NDIM_PTR(retval), (char *) &ndim, sizeof(int));
|
||||
retval->size = nbytes;
|
||||
retval->ndim = ndim;
|
||||
SET_LO_FLAG(false, retval);
|
||||
memmove((char *) ARR_DIMS(retval), (char *) dim, ndim * sizeof(int));
|
||||
memmove((char *) ARR_LBOUND(retval), (char *) lBound,
|
||||
ndim * sizeof(int));
|
||||
memcpy((char *) ARR_DIMS(retval), (char *) dim,
|
||||
ndim * sizeof(int));
|
||||
memcpy((char *) ARR_LBOUND(retval), (char *) lBound,
|
||||
ndim * sizeof(int));
|
||||
|
||||
/*
|
||||
* dataPtr is an array of arbitraystuff even though its type is
|
||||
* char* cast to char** to pass to _CopyArrayEls for now - jolly
|
||||
*/
|
||||
_CopyArrayEls((char **) dataPtr,
|
||||
ARR_DATA_PTR(retval), nitems,
|
||||
typlen, typalign, typbyval);
|
||||
CopyArrayEls(ARR_DATA_PTR(retval), dataPtr, nitems,
|
||||
typbyval, typlen, typalign, true);
|
||||
pfree(dataPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -226,8 +221,8 @@ array_in(PG_FUNCTION_ARGS)
|
||||
nbytes = bytes + ARR_OVERHEAD(ndim);
|
||||
retval = (ArrayType *) palloc(nbytes);
|
||||
MemSet(retval, 0, nbytes);
|
||||
memmove(retval, (char *) &nbytes, sizeof(int));
|
||||
memmove((char *) ARR_NDIM_PTR(retval), (char *) &ndim, sizeof(int));
|
||||
retval->size = nbytes;
|
||||
retval->ndim = ndim;
|
||||
SET_LO_FLAG(true, retval);
|
||||
SET_CHUNK_FLAG(chunked, retval);
|
||||
memmove((char *) ARR_DIMS(retval), (char *) dim, ndim * sizeof(int));
|
||||
@ -238,7 +233,7 @@ array_in(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
pfree(string_save);
|
||||
PG_RETURN_POINTER(retval);
|
||||
PG_RETURN_ARRAYTYPE_P(retval);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
@ -331,50 +326,51 @@ _ArrayCount(char *str, int *dim, int typdelim)
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* _ReadArrayStr :
|
||||
* parses the array string pointed by "arrayStr" and converts it in the
|
||||
* ReadArrayStr :
|
||||
* parses the array string pointed by "arrayStr" and converts it to
|
||||
* internal format. The external format expected is like C array
|
||||
* declaration. Unspecified elements are initialized to zero for fixed length
|
||||
* base types and to empty varlena structures for variable length base
|
||||
* types.
|
||||
* result :
|
||||
* returns the internal representation of the array elements
|
||||
* nbytes is set to the size of the array in its internal representation.
|
||||
* returns a palloc'd array of Datum representations of the array elements.
|
||||
* If element type is pass-by-ref, the Datums point to palloc'd values.
|
||||
* *nbytes is set to the amount of data space needed for the array,
|
||||
* including alignment padding but not including array header overhead.
|
||||
*---------------------------------------------------------------------------
|
||||
*/
|
||||
static char *
|
||||
_ReadArrayStr(char *arrayStr,
|
||||
int nitems,
|
||||
int ndim,
|
||||
int *dim,
|
||||
FmgrInfo *inputproc, /* function used for the
|
||||
* conversion */
|
||||
Oid typelem,
|
||||
int32 typmod,
|
||||
char typdelim,
|
||||
int typlen,
|
||||
bool typbyval,
|
||||
char typalign,
|
||||
int *nbytes)
|
||||
static Datum *
|
||||
ReadArrayStr(char *arrayStr,
|
||||
int nitems,
|
||||
int ndim,
|
||||
int *dim,
|
||||
FmgrInfo *inputproc,
|
||||
Oid typelem,
|
||||
int32 typmod,
|
||||
char typdelim,
|
||||
int typlen,
|
||||
bool typbyval,
|
||||
char typalign,
|
||||
int *nbytes)
|
||||
{
|
||||
int i,
|
||||
nest_level = 0;
|
||||
Datum *values;
|
||||
char *p,
|
||||
*q,
|
||||
*r,
|
||||
**values;
|
||||
*r;
|
||||
bool scanning_string = false;
|
||||
int indx[MAXDIM],
|
||||
prod[MAXDIM];
|
||||
bool eoArray = false;
|
||||
|
||||
mda_get_prod(ndim, dim, prod);
|
||||
for (i = 0; i < ndim; indx[i++] = 0);
|
||||
/* read array enclosed within {} */
|
||||
values = (char **) palloc(nitems * sizeof(char *));
|
||||
MemSet(values, 0, nitems * sizeof(char *));
|
||||
values = (Datum *) palloc(nitems * sizeof(Datum));
|
||||
MemSet(values, 0, nitems * sizeof(Datum));
|
||||
MemSet(indx, 0, sizeof(indx));
|
||||
q = p = arrayStr;
|
||||
|
||||
/* read array enclosed within {} */
|
||||
while (!eoArray)
|
||||
{
|
||||
bool done = false;
|
||||
@ -442,53 +438,56 @@ _ReadArrayStr(char *arrayStr,
|
||||
*q = '\0';
|
||||
if (i >= nitems)
|
||||
elog(ERROR, "array_in: illformed array constant");
|
||||
values[i] = (char *) FunctionCall3(inputproc,
|
||||
CStringGetDatum(p),
|
||||
ObjectIdGetDatum(typelem),
|
||||
Int32GetDatum(typmod));
|
||||
values[i] = FunctionCall3(inputproc,
|
||||
CStringGetDatum(p),
|
||||
ObjectIdGetDatum(typelem),
|
||||
Int32GetDatum(typmod));
|
||||
p = ++q;
|
||||
/*
|
||||
* if not at the end of the array skip white space
|
||||
*/
|
||||
if (!eoArray)
|
||||
|
||||
/*
|
||||
* if not at the end of the array skip white space
|
||||
*/
|
||||
while (isspace((int) *q))
|
||||
{
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Initialize any unset items and compute total data space needed
|
||||
*/
|
||||
if (typlen > 0)
|
||||
{
|
||||
*nbytes = nitems * typlen;
|
||||
if (!typbyval)
|
||||
for (i = 0; i < nitems; i++)
|
||||
if (!values[i])
|
||||
if (values[i] == (Datum) 0)
|
||||
{
|
||||
values[i] = palloc(typlen);
|
||||
MemSet(values[i], 0, typlen);
|
||||
values[i] = PointerGetDatum(palloc(typlen));
|
||||
MemSet(DatumGetPointer(values[i]), 0, typlen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0, *nbytes = 0; i < nitems; i++)
|
||||
*nbytes = 0;
|
||||
for (i = 0; i < nitems; i++)
|
||||
{
|
||||
if (values[i])
|
||||
if (values[i] != (Datum) 0)
|
||||
{
|
||||
if (typalign == 'd')
|
||||
*nbytes += MAXALIGN(*(int32 *) values[i]);
|
||||
*nbytes += MAXALIGN(VARSIZE(DatumGetPointer(values[i])));
|
||||
else
|
||||
*nbytes += INTALIGN(*(int32 *) values[i]);
|
||||
*nbytes += INTALIGN(VARSIZE(DatumGetPointer(values[i])));
|
||||
}
|
||||
else
|
||||
{
|
||||
*nbytes += sizeof(int32);
|
||||
values[i] = palloc(sizeof(int32));
|
||||
*(int32 *) values[i] = sizeof(int32);
|
||||
values[i] = PointerGetDatum(palloc(sizeof(int32)));
|
||||
VARATT_SIZEP(DatumGetPointer(values[i])) = sizeof(int32);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (char *) values;
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
@ -565,26 +564,39 @@ _ReadLOArray(char *str,
|
||||
|
||||
#endif
|
||||
|
||||
/*----------
|
||||
* Copy data into an array object from a temporary array of Datums.
|
||||
*
|
||||
* p: pointer to start of array data area
|
||||
* values: array of Datums to be copied
|
||||
* nitems: number of Datums to be copied
|
||||
* typbyval, typlen, typalign: info about element datatype
|
||||
* freedata: if TRUE and element type is pass-by-ref, pfree data values
|
||||
* referenced by Datums after copying them.
|
||||
*----------
|
||||
*/
|
||||
static void
|
||||
_CopyArrayEls(char **values,
|
||||
char *p,
|
||||
int nitems,
|
||||
int typlen,
|
||||
char typalign,
|
||||
bool typbyval)
|
||||
CopyArrayEls(char *p,
|
||||
Datum *values,
|
||||
int nitems,
|
||||
bool typbyval,
|
||||
int typlen,
|
||||
char typalign,
|
||||
bool freedata)
|
||||
{
|
||||
int i;
|
||||
int inc;
|
||||
|
||||
if (typbyval)
|
||||
freedata = false;
|
||||
|
||||
for (i = 0; i < nitems; i++)
|
||||
{
|
||||
int inc;
|
||||
|
||||
inc = ArrayCastAndSet((Datum) values[i], typbyval, typlen, p);
|
||||
inc = ArrayCastAndSet(values[i], typbyval, typlen, p);
|
||||
p += inc;
|
||||
if (!typbyval)
|
||||
pfree(values[i]);
|
||||
if (freedata)
|
||||
pfree(DatumGetPointer(values[i]));
|
||||
}
|
||||
pfree(values);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
@ -596,7 +608,7 @@ _CopyArrayEls(char **values,
|
||||
Datum
|
||||
array_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *v = (ArrayType *) PG_GETARG_VARLENA_P(0);
|
||||
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Oid element_type = PG_GETARG_OID(1);
|
||||
int typlen;
|
||||
bool typbyval;
|
||||
@ -786,7 +798,7 @@ array_out(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
array_dims(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *v = (ArrayType *) PG_GETARG_VARLENA_P(0);
|
||||
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
|
||||
text *result;
|
||||
char *p;
|
||||
int nbytes,
|
||||
@ -821,8 +833,8 @@ array_dims(PG_FUNCTION_ARGS)
|
||||
/*---------------------------------------------------------------------------
|
||||
* array_ref :
|
||||
* This routine takes an array pointer and an index array and returns
|
||||
* a pointer to the referred element if element is passed by
|
||||
* reference otherwise returns the value of the referred element.
|
||||
* the referenced item as a Datum. Note that for a pass-by-reference
|
||||
* datatype, the returned Datum is a pointer into the array object.
|
||||
*---------------------------------------------------------------------------
|
||||
*/
|
||||
Datum
|
||||
@ -905,7 +917,7 @@ array_ref(ArrayType *array,
|
||||
{ /* not by value */
|
||||
char *tempdata = palloc(elmlen);
|
||||
|
||||
memmove(tempdata, DatumGetPointer(result), elmlen);
|
||||
memcpy(tempdata, DatumGetPointer(result), elmlen);
|
||||
result = PointerGetDatum(tempdata);
|
||||
}
|
||||
pfree(v);
|
||||
@ -1003,14 +1015,15 @@ array_clip(ArrayType *array,
|
||||
#endif
|
||||
bytes = strlen(newname) + 1 + ARR_OVERHEAD(nSubscripts);
|
||||
newArr = (ArrayType *) palloc(bytes);
|
||||
memmove(newArr, array, sizeof(ArrayType));
|
||||
memmove(newArr, &bytes, sizeof(int));
|
||||
memmove(ARR_DIMS(newArr), span, nSubscripts * sizeof(int));
|
||||
memmove(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int));
|
||||
newArr->size = bytes;
|
||||
newArr->ndim = array->ndim;
|
||||
newArr->flags = array->flags;
|
||||
memcpy(ARR_DIMS(newArr), span, nSubscripts * sizeof(int));
|
||||
memcpy(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int));
|
||||
strcpy(ARR_DATA_PTR(newArr), newname);
|
||||
|
||||
rsize = compute_size(lowerIndx, upperIndx, nSubscripts, elmlen);
|
||||
if (rsize < MAX_BUFF_SIZE)
|
||||
if (rsize < BLCKSZ)
|
||||
{
|
||||
char *buff;
|
||||
|
||||
@ -1072,10 +1085,11 @@ array_clip(ArrayType *array,
|
||||
bytes += ARR_OVERHEAD(nSubscripts);
|
||||
}
|
||||
newArr = (ArrayType *) palloc(bytes);
|
||||
memmove(newArr, array, sizeof(ArrayType));
|
||||
memmove(newArr, &bytes, sizeof(int));
|
||||
memmove(ARR_DIMS(newArr), span, nSubscripts * sizeof(int));
|
||||
memmove(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int));
|
||||
newArr->size = bytes;
|
||||
newArr->ndim = array->ndim;
|
||||
newArr->flags = array->flags;
|
||||
memcpy(ARR_DIMS(newArr), span, nSubscripts * sizeof(int));
|
||||
memcpy(ARR_LBOUND(newArr), lowerIndx, nSubscripts * sizeof(int));
|
||||
_ArrayRange(lowerIndx, upperIndx, elmlen, ARR_DATA_PTR(newArr), array, 1);
|
||||
return newArr;
|
||||
}
|
||||
@ -1322,7 +1336,7 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||
{
|
||||
ArrayType *v;
|
||||
ArrayType *result;
|
||||
char **values;
|
||||
Datum *values;
|
||||
char *elt;
|
||||
int *dim;
|
||||
int ndim;
|
||||
@ -1338,14 +1352,13 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||
Oid proc;
|
||||
char typalign;
|
||||
char *s;
|
||||
char *p;
|
||||
|
||||
/* Get input array */
|
||||
if (fcinfo->nargs < 1)
|
||||
elog(ERROR, "array_map: invalid nargs: %d", fcinfo->nargs);
|
||||
if (PG_ARGISNULL(0))
|
||||
elog(ERROR, "array_map: null input array");
|
||||
v = (ArrayType *) PG_GETARG_VARLENA_P(0);
|
||||
v = PG_GETARG_ARRAYTYPE_P(0);
|
||||
|
||||
/* Large objects not yet supported */
|
||||
if (ARR_IS_LO(v) == true)
|
||||
@ -1357,7 +1370,7 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||
|
||||
/* Check for empty array */
|
||||
if (nitems <= 0)
|
||||
PG_RETURN_POINTER(v);
|
||||
PG_RETURN_ARRAYTYPE_P(v);
|
||||
|
||||
/* Lookup source and result types. Unneeded variables are reused. */
|
||||
system_cache_lookup(inpType, false, &inp_typlen, &inp_typbyval,
|
||||
@ -1366,8 +1379,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||
&typdelim, &typelem, &proc, &typalign);
|
||||
|
||||
/* Allocate temporary array for new values */
|
||||
values = (char **) palloc(nitems * sizeof(char *));
|
||||
MemSet(values, 0, nitems * sizeof(char *));
|
||||
values = (Datum *) palloc(nitems * sizeof(Datum));
|
||||
MemSet(values, 0, nitems * sizeof(Datum));
|
||||
|
||||
/* Loop over source data */
|
||||
s = (char *) ARR_DATA_PTR(v);
|
||||
@ -1411,30 +1424,16 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||
fcinfo->arg[0] = (Datum) elt;
|
||||
fcinfo->argnull[0] = false;
|
||||
fcinfo->isnull = false;
|
||||
p = (char *) FunctionCallInvoke(fcinfo);
|
||||
values[i] = FunctionCallInvoke(fcinfo);
|
||||
if (fcinfo->isnull)
|
||||
elog(ERROR, "array_map: cannot handle NULL in array");
|
||||
|
||||
/* Update values and total result size */
|
||||
/* Update total result size */
|
||||
if (typbyval)
|
||||
{
|
||||
values[i] = p;
|
||||
nbytes += typlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
int len;
|
||||
|
||||
len = ((typlen > 0) ? typlen : INTALIGN(*(int32 *) p));
|
||||
/* Needed because _CopyArrayEls tries to pfree items */
|
||||
if (p == elt)
|
||||
{
|
||||
p = (char *) palloc(len);
|
||||
memcpy(p, elt, len);
|
||||
}
|
||||
values[i] = p;
|
||||
nbytes += len;
|
||||
}
|
||||
nbytes += ((typlen > 0) ? typlen :
|
||||
INTALIGN(VARSIZE(DatumGetPointer(values[i]))));
|
||||
}
|
||||
|
||||
/* Allocate and initialize the result array */
|
||||
@ -1442,18 +1441,130 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||
result = (ArrayType *) palloc(nbytes);
|
||||
MemSet(result, 0, nbytes);
|
||||
|
||||
memcpy((char *) result, (char *) &nbytes, sizeof(int));
|
||||
memcpy((char *) ARR_NDIM_PTR(result), (char *) &ndim, sizeof(int));
|
||||
memcpy((char *) ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
|
||||
result->size = nbytes;
|
||||
result->ndim = ndim;
|
||||
memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
|
||||
|
||||
/* Copy new values into the result array. values is pfreed. */
|
||||
_CopyArrayEls((char **) values,
|
||||
ARR_DATA_PTR(result), nitems,
|
||||
typlen, typalign, typbyval);
|
||||
/* Note: do not risk trying to pfree the results of the called function */
|
||||
CopyArrayEls(ARR_DATA_PTR(result), values, nitems,
|
||||
typbyval, typlen, typalign, false);
|
||||
pfree(values);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
PG_RETURN_ARRAYTYPE_P(result);
|
||||
}
|
||||
|
||||
/*----------
|
||||
* construct_array --- simple method for constructing an array object
|
||||
*
|
||||
* elems: array of Datum items to become the array contents
|
||||
* nelems: number of items
|
||||
* elmbyval, elmlen, elmalign: info for the datatype of the items
|
||||
*
|
||||
* A palloc'd 1-D array object is constructed and returned. Note that
|
||||
* elem values will be copied into the object even if pass-by-ref type.
|
||||
* NULL element values are not supported.
|
||||
*----------
|
||||
*/
|
||||
ArrayType *
|
||||
construct_array(Datum *elems, int nelems,
|
||||
bool elmbyval, int elmlen, char elmalign)
|
||||
{
|
||||
ArrayType *result;
|
||||
int nbytes;
|
||||
int i;
|
||||
|
||||
if (elmlen > 0)
|
||||
{
|
||||
/* XXX what about alignment? */
|
||||
nbytes = elmlen * nelems;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* varlena type */
|
||||
nbytes = 0;
|
||||
for (i = 0; i < nelems; i++)
|
||||
nbytes += INTALIGN(VARSIZE(DatumGetPointer(elems[i])));
|
||||
}
|
||||
|
||||
/* Allocate and initialize 1-D result array */
|
||||
nbytes += ARR_OVERHEAD(1);
|
||||
result = (ArrayType *) palloc(nbytes);
|
||||
|
||||
result->size = nbytes;
|
||||
result->ndim = 1;
|
||||
result->flags = 0;
|
||||
ARR_DIMS(result)[0] = nelems;
|
||||
ARR_LBOUND(result)[0] = 1;
|
||||
|
||||
CopyArrayEls(ARR_DATA_PTR(result), elems, nelems,
|
||||
elmbyval, elmlen, elmalign, false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*----------
|
||||
* deconstruct_array --- simple method for extracting data from an array
|
||||
*
|
||||
* array: array object to examine (must not be NULL)
|
||||
* elmbyval, elmlen, elmalign: info for the datatype of the items
|
||||
* elemsp: return value, set to point to palloc'd array of Datum values
|
||||
* nelemsp: return value, set to number of extracted values
|
||||
*
|
||||
* If array elements are pass-by-ref data type, the returned Datums will
|
||||
* be pointers into the array object.
|
||||
*----------
|
||||
*/
|
||||
void
|
||||
deconstruct_array(ArrayType *array,
|
||||
bool elmbyval, int elmlen, char elmalign,
|
||||
Datum **elemsp, int *nelemsp)
|
||||
{
|
||||
Datum *elems;
|
||||
int nelems;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
nelems = getNitems(ARR_NDIM(array), ARR_DIMS(array));
|
||||
if (nelems <= 0)
|
||||
{
|
||||
*elemsp = NULL;
|
||||
*nelemsp = 0;
|
||||
return;
|
||||
}
|
||||
*elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
|
||||
*nelemsp = nelems;
|
||||
|
||||
p = ARR_DATA_PTR(array);
|
||||
for (i = 0; i < nelems; i++)
|
||||
{
|
||||
if (elmbyval)
|
||||
{
|
||||
switch (elmlen)
|
||||
{
|
||||
case 1:
|
||||
elems[i] = CharGetDatum(*p);
|
||||
break;
|
||||
case 2:
|
||||
elems[i] = Int16GetDatum(*(int16 *) p);
|
||||
break;
|
||||
case 4:
|
||||
elems[i] = Int32GetDatum(*(int32 *) p);
|
||||
break;
|
||||
}
|
||||
p += elmlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
elems[i] = PointerGetDatum(p);
|
||||
if (elmlen > 0)
|
||||
p += elmlen;
|
||||
else
|
||||
p += INTALIGN(VARSIZE(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* array_eq :
|
||||
* compares two arrays for equality
|
||||
@ -1464,8 +1575,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||
Datum
|
||||
array_eq(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *array1 = (ArrayType *) PG_GETARG_VARLENA_P(0);
|
||||
ArrayType *array2 = (ArrayType *) PG_GETARG_VARLENA_P(1);
|
||||
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
|
||||
|
||||
if (*(int32 *) array1 != *(int32 *) array2)
|
||||
PG_RETURN_BOOL(false);
|
||||
@ -1493,14 +1604,11 @@ system_cache_lookup(Oid element_type,
|
||||
typeTuple = SearchSysCacheTuple(TYPEOID,
|
||||
ObjectIdGetDatum(element_type),
|
||||
0, 0, 0);
|
||||
|
||||
if (!HeapTupleIsValid(typeTuple))
|
||||
{
|
||||
elog(ERROR, "array_out: Cache lookup failed for type %u\n",
|
||||
elog(ERROR, "array_out: Cache lookup failed for type %u",
|
||||
element_type);
|
||||
return;
|
||||
}
|
||||
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
|
||||
|
||||
*typlen = typeStruct->typlen;
|
||||
*typbyval = typeStruct->typbyval;
|
||||
*typdelim = typeStruct->typdelim;
|
||||
@ -1536,7 +1644,11 @@ _ArrayCast(char *value, bool byval, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Copy datum to *dest and return total space used (including align padding)
|
||||
*
|
||||
* XXX this routine needs to be told typalign too!
|
||||
*/
|
||||
static int
|
||||
ArrayCastAndSet(Datum src,
|
||||
bool typbyval,
|
||||
@ -1560,16 +1672,26 @@ ArrayCastAndSet(Datum src,
|
||||
case 4:
|
||||
*(int32 *) dest = DatumGetInt32(src);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "ArrayCastAndSet: unexpected typlen");
|
||||
break;
|
||||
}
|
||||
/* For by-val types, assume no alignment padding is needed */
|
||||
inc = typlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(dest, DatumGetPointer(src), typlen);
|
||||
inc = typlen;
|
||||
/* XXX WRONG: need to consider type's alignment requirement */
|
||||
inc = typlen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(dest, DatumGetPointer(src), *(int32 *) DatumGetPointer(src));
|
||||
inc = (INTALIGN(*(int32 *) DatumGetPointer(src)));
|
||||
/* varlena type */
|
||||
memmove(dest, DatumGetPointer(src), VARSIZE(DatumGetPointer(src)));
|
||||
/* XXX WRONG: should use MAXALIGN or type's alignment requirement */
|
||||
inc = INTALIGN(VARSIZE(DatumGetPointer(src)));
|
||||
}
|
||||
return inc;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.64 2000/07/12 22:59:08 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.65 2000/07/17 03:05:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,7 +17,7 @@
|
||||
* Basic float4 ops:
|
||||
* float4in, float4out, float4abs, float4um
|
||||
* Basic float8 ops:
|
||||
* float8in, float8inAd, float8out, float8outAd, float8abs, float8um
|
||||
* float8in, float8out, float8abs, float8um
|
||||
* Arithmetic operators:
|
||||
* float4pl, float4mi, float4mul, float4div
|
||||
* float8pl, float8mi, float8mul, float8div
|
||||
@ -64,6 +64,7 @@
|
||||
#endif
|
||||
|
||||
#include "fmgr.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
static void CheckFloat8Val(double val);
|
||||
@ -90,7 +91,6 @@ static void CheckFloat8Val(double val);
|
||||
|
||||
#ifndef atof
|
||||
extern double atof(const char *p);
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_CBRT
|
||||
@ -100,9 +100,8 @@ static double cbrt(double x);
|
||||
#else
|
||||
#if !defined(nextstep)
|
||||
extern double cbrt(double x);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif /* HAVE_CBRT */
|
||||
|
||||
#ifndef HAVE_RINT
|
||||
#define rint my_rint
|
||||
@ -110,10 +109,9 @@ static double rint(double x);
|
||||
|
||||
#else
|
||||
extern double rint(double x);
|
||||
#endif /* HAVE_RINT */
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif /* NeXT check */
|
||||
|
||||
/* ========== USER I/O ROUTINES ========== */
|
||||
|
||||
@ -453,7 +451,6 @@ float8smaller(float64 arg1, float64 arg2)
|
||||
* float4mi - returns a pointer to arg1 - arg2
|
||||
* float4mul - returns a pointer to arg1 * arg2
|
||||
* float4div - returns a pointer to arg1 / arg2
|
||||
* float4inc - returns a poniter to arg1 + 1.0
|
||||
*/
|
||||
float32
|
||||
float4pl(float32 arg1, float32 arg2)
|
||||
@ -527,29 +524,11 @@ float4div(float32 arg1, float32 arg2)
|
||||
return result;
|
||||
}
|
||||
|
||||
float32
|
||||
float4inc(float32 arg1)
|
||||
{
|
||||
float32 result;
|
||||
double val;
|
||||
|
||||
if (!arg1)
|
||||
return (float32) NULL;
|
||||
|
||||
val = *arg1 + (float32data) 1.0;
|
||||
|
||||
CheckFloat4Val(val);
|
||||
result = (float32) palloc(sizeof(float32data));
|
||||
*result = val;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* float8pl - returns a pointer to arg1 + arg2
|
||||
* float8mi - returns a pointer to arg1 - arg2
|
||||
* float8mul - returns a pointer to arg1 * arg2
|
||||
* float8div - returns a pointer to arg1 / arg2
|
||||
* float8inc - returns a pointer to arg1 + 1.0
|
||||
*/
|
||||
float64
|
||||
float8pl(float64 arg1, float64 arg2)
|
||||
@ -622,22 +601,6 @@ float8div(float64 arg1, float64 arg2)
|
||||
return result;
|
||||
}
|
||||
|
||||
float64
|
||||
float8inc(float64 arg1)
|
||||
{
|
||||
float64 result;
|
||||
double val;
|
||||
|
||||
if (!arg1)
|
||||
return (float64) NULL;
|
||||
|
||||
val = *arg1 + (float64data) 1.0;
|
||||
CheckFloat8Val(val);
|
||||
result = (float64) palloc(sizeof(float64data));
|
||||
*result = val;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
@ -1572,10 +1535,181 @@ setseed(float64 seed)
|
||||
} /* setseed() */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ====================
|
||||
* ARITHMETIC OPERATORS
|
||||
* ====================
|
||||
* =========================
|
||||
* FLOAT AGGREGATE OPERATORS
|
||||
* =========================
|
||||
*
|
||||
* float8_accum - accumulate for AVG(), STDDEV(), etc
|
||||
* float4_accum - same, but input data is float4
|
||||
* float8_avg - produce final result for float AVG()
|
||||
* float8_variance - produce final result for float VARIANCE()
|
||||
* float8_stddev - produce final result for float STDDEV()
|
||||
*
|
||||
* The transition datatype for all these aggregates is a 3-element array
|
||||
* of float8, holding the values N, sum(X), sum(X*X) in that order.
|
||||
*
|
||||
* Note that we represent N as a float to avoid having to build a special
|
||||
* datatype. Given a reasonable floating-point implementation, there should
|
||||
* be no accuracy loss unless N exceeds 2 ^ 52 or so (by which time the
|
||||
* user will have doubtless lost interest anyway...)
|
||||
*/
|
||||
|
||||
static float8 *
|
||||
check_float8_array(ArrayType *transarray, const char *caller)
|
||||
{
|
||||
/*
|
||||
* We expect the input to be a 3-element float array; verify that.
|
||||
* We don't need to use deconstruct_array() since the array data
|
||||
* is just going to look like a C array of 3 float8 values.
|
||||
*/
|
||||
if (ARR_SIZE(transarray) != (ARR_OVERHEAD(1) + 3 * sizeof(float8)) ||
|
||||
ARR_NDIM(transarray) != 1 ||
|
||||
ARR_DIMS(transarray)[0] != 3)
|
||||
elog(ERROR, "%s: expected 3-element float8 array", caller);
|
||||
return (float8 *) ARR_DATA_PTR(transarray);
|
||||
}
|
||||
|
||||
Datum
|
||||
float8_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
float8 newval = PG_GETARG_FLOAT8(1);
|
||||
float8 *transvalues;
|
||||
float8 N,
|
||||
sumX,
|
||||
sumX2;
|
||||
Datum transdatums[3];
|
||||
ArrayType *result;
|
||||
|
||||
transvalues = check_float8_array(transarray, "float8_accum");
|
||||
N = transvalues[0];
|
||||
sumX = transvalues[1];
|
||||
sumX2 = transvalues[2];
|
||||
|
||||
N += 1.0;
|
||||
sumX += newval;
|
||||
sumX2 += newval * newval;
|
||||
|
||||
transdatums[0] = Float8GetDatumFast(N);
|
||||
transdatums[1] = Float8GetDatumFast(sumX);
|
||||
transdatums[2] = Float8GetDatumFast(sumX2);
|
||||
|
||||
result = construct_array(transdatums, 3,
|
||||
false /* float8 byval */, sizeof(float8), 'd');
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(result);
|
||||
}
|
||||
|
||||
Datum
|
||||
float4_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
float4 newval4 = PG_GETARG_FLOAT4(1);
|
||||
float8 *transvalues;
|
||||
float8 N,
|
||||
sumX,
|
||||
sumX2,
|
||||
newval;
|
||||
Datum transdatums[3];
|
||||
ArrayType *result;
|
||||
|
||||
transvalues = check_float8_array(transarray, "float4_accum");
|
||||
N = transvalues[0];
|
||||
sumX = transvalues[1];
|
||||
sumX2 = transvalues[2];
|
||||
|
||||
/* Do arithmetic in float8 for best accuracy */
|
||||
newval = newval4;
|
||||
|
||||
N += 1.0;
|
||||
sumX += newval;
|
||||
sumX2 += newval * newval;
|
||||
|
||||
transdatums[0] = Float8GetDatumFast(N);
|
||||
transdatums[1] = Float8GetDatumFast(sumX);
|
||||
transdatums[2] = Float8GetDatumFast(sumX2);
|
||||
|
||||
result = construct_array(transdatums, 3,
|
||||
false /* float8 byval */, sizeof(float8), 'd');
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(result);
|
||||
}
|
||||
|
||||
Datum
|
||||
float8_avg(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
float8 *transvalues;
|
||||
float8 N,
|
||||
sumX;
|
||||
|
||||
transvalues = check_float8_array(transarray, "float8_avg");
|
||||
N = transvalues[0];
|
||||
sumX = transvalues[1];
|
||||
/* ignore sumX2 */
|
||||
|
||||
/* SQL92 defines AVG of no values to be NULL */
|
||||
if (N == 0.0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
PG_RETURN_FLOAT8(sumX / N);
|
||||
}
|
||||
|
||||
Datum
|
||||
float8_variance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
float8 *transvalues;
|
||||
float8 N,
|
||||
sumX,
|
||||
sumX2;
|
||||
|
||||
transvalues = check_float8_array(transarray, "float8_variance");
|
||||
N = transvalues[0];
|
||||
sumX = transvalues[1];
|
||||
sumX2 = transvalues[2];
|
||||
|
||||
/* We define VARIANCE of no values to be NULL, of 1 value to be 0 */
|
||||
if (N == 0.0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
if (N <= 1.0)
|
||||
PG_RETURN_FLOAT8(0.0);
|
||||
|
||||
PG_RETURN_FLOAT8((N * sumX2 - sumX * sumX) / (N * (N - 1.0)));
|
||||
}
|
||||
|
||||
Datum
|
||||
float8_stddev(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
float8 *transvalues;
|
||||
float8 N,
|
||||
sumX,
|
||||
sumX2;
|
||||
|
||||
transvalues = check_float8_array(transarray, "float8_stddev");
|
||||
N = transvalues[0];
|
||||
sumX = transvalues[1];
|
||||
sumX2 = transvalues[2];
|
||||
|
||||
/* We define STDDEV of no values to be NULL, of 1 value to be 0 */
|
||||
if (N == 0.0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
if (N <= 1.0)
|
||||
PG_RETURN_FLOAT8(0.0);
|
||||
|
||||
PG_RETURN_FLOAT8(sqrt((N * sumX2 - sumX * sumX) / (N * (N - 1.0))));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ====================================
|
||||
* MIXED-PRECISION ARITHMETIC OPERATORS
|
||||
* ====================================
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.40 2000/07/12 22:59:08 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.41 2000/07/17 03:05:17 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -650,14 +650,6 @@ int2div(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_INT16(arg1 / arg2);
|
||||
}
|
||||
|
||||
Datum
|
||||
int2inc(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int16 arg = PG_GETARG_INT16(0);
|
||||
|
||||
PG_RETURN_INT16(arg + 1);
|
||||
}
|
||||
|
||||
Datum
|
||||
int24pl(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
*
|
||||
* 1998 Jan Wieck
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.31 2000/06/15 03:32:29 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.32 2000/07/17 03:05:18 tgl Exp $
|
||||
*
|
||||
* ----------
|
||||
*/
|
||||
@ -18,6 +18,7 @@
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/int8.h"
|
||||
#include "utils/numeric.h"
|
||||
@ -1230,49 +1231,6 @@ numeric_inc(Numeric num)
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* numeric_dec() -
|
||||
*
|
||||
* Decrement a number by one
|
||||
* ----------
|
||||
*/
|
||||
Numeric
|
||||
numeric_dec(Numeric num)
|
||||
{
|
||||
NumericVar arg;
|
||||
Numeric res;
|
||||
|
||||
/* ----------
|
||||
* Handle NULL
|
||||
* ----------
|
||||
*/
|
||||
if (num == NULL)
|
||||
return NULL;
|
||||
|
||||
/* ----------
|
||||
* Handle NaN
|
||||
* ----------
|
||||
*/
|
||||
if (NUMERIC_IS_NAN(num))
|
||||
return make_result(&const_nan);
|
||||
|
||||
/* ----------
|
||||
* Compute the result and return it
|
||||
* ----------
|
||||
*/
|
||||
init_var(&arg);
|
||||
|
||||
set_var_from_num(num, &arg);
|
||||
|
||||
sub_var(&arg, &const_one, &arg);
|
||||
res = make_result(&arg);
|
||||
|
||||
free_var(&arg);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* numeric_smaller() -
|
||||
*
|
||||
@ -1733,24 +1691,24 @@ numeric_int4(Numeric num)
|
||||
}
|
||||
|
||||
|
||||
Numeric
|
||||
int8_numeric(int64 *val)
|
||||
Datum
|
||||
int8_numeric(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum val = PG_GETARG_DATUM(0);
|
||||
Numeric res;
|
||||
NumericVar result;
|
||||
char *tmp;
|
||||
|
||||
init_var(&result);
|
||||
|
||||
tmp = DatumGetCString(DirectFunctionCall1(int8out,
|
||||
PointerGetDatum(val)));
|
||||
tmp = DatumGetCString(DirectFunctionCall1(int8out, val));
|
||||
set_var_from_str(tmp, &result);
|
||||
res = make_result(&result);
|
||||
|
||||
free_var(&result);
|
||||
pfree(tmp);
|
||||
|
||||
return res;
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
@ -1939,6 +1897,369 @@ numeric_float4(Numeric num)
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
*
|
||||
* Aggregate functions
|
||||
*
|
||||
* The transition datatype for all these aggregates is a 3-element array
|
||||
* of Numeric, holding the values N, sum(X), sum(X*X) in that order.
|
||||
*
|
||||
* We represent N as a numeric mainly to avoid having to build a special
|
||||
* datatype; it's unlikely it'd overflow an int4, but ...
|
||||
*
|
||||
* ----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static ArrayType *
|
||||
do_numeric_accum(ArrayType *transarray, Numeric newval)
|
||||
{
|
||||
Datum *transdatums;
|
||||
int ndatums;
|
||||
Numeric N,
|
||||
sumX,
|
||||
sumX2;
|
||||
ArrayType *result;
|
||||
|
||||
/* We assume the input is array of numeric */
|
||||
deconstruct_array(transarray,
|
||||
false, -1, 'i',
|
||||
&transdatums, &ndatums);
|
||||
if (ndatums != 3)
|
||||
elog(ERROR, "do_numeric_accum: expected 3-element numeric array");
|
||||
N = DatumGetNumeric(transdatums[0]);
|
||||
sumX = DatumGetNumeric(transdatums[1]);
|
||||
sumX2 = DatumGetNumeric(transdatums[2]);
|
||||
|
||||
N = numeric_inc(N);
|
||||
sumX = numeric_add(sumX, newval);
|
||||
sumX2 = numeric_add(sumX2, numeric_mul(newval, newval));
|
||||
|
||||
transdatums[0] = NumericGetDatum(N);
|
||||
transdatums[1] = NumericGetDatum(sumX);
|
||||
transdatums[2] = NumericGetDatum(sumX2);
|
||||
|
||||
result = construct_array(transdatums, 3,
|
||||
false, -1, 'i');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Datum
|
||||
numeric_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Numeric newval = PG_GETARG_NUMERIC(1);
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
|
||||
}
|
||||
|
||||
/*
|
||||
* Integer data types all use Numeric accumulators to share code and
|
||||
* avoid risk of overflow.
|
||||
*/
|
||||
|
||||
Datum
|
||||
int2_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Datum newval2 = PG_GETARG_DATUM(1);
|
||||
Numeric newval;
|
||||
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
|
||||
}
|
||||
|
||||
Datum
|
||||
int4_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Datum newval4 = PG_GETARG_DATUM(1);
|
||||
Numeric newval;
|
||||
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
|
||||
}
|
||||
|
||||
Datum
|
||||
int8_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Datum newval8 = PG_GETARG_DATUM(1);
|
||||
Numeric newval;
|
||||
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
|
||||
}
|
||||
|
||||
Datum
|
||||
numeric_avg(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Datum *transdatums;
|
||||
int ndatums;
|
||||
Numeric N,
|
||||
sumX;
|
||||
|
||||
/* We assume the input is array of numeric */
|
||||
deconstruct_array(transarray,
|
||||
false, -1, 'i',
|
||||
&transdatums, &ndatums);
|
||||
if (ndatums != 3)
|
||||
elog(ERROR, "numeric_avg: expected 3-element numeric array");
|
||||
N = DatumGetNumeric(transdatums[0]);
|
||||
sumX = DatumGetNumeric(transdatums[1]);
|
||||
/* ignore sumX2 */
|
||||
|
||||
/* SQL92 defines AVG of no values to be NULL */
|
||||
/* N is zero iff no digits (cf. numeric_uminus) */
|
||||
if (N->varlen == NUMERIC_HDRSZ)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
PG_RETURN_NUMERIC(numeric_div(sumX, N));
|
||||
}
|
||||
|
||||
Datum
|
||||
numeric_variance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Datum *transdatums;
|
||||
int ndatums;
|
||||
Numeric N,
|
||||
sumX,
|
||||
sumX2,
|
||||
res;
|
||||
NumericVar vN,
|
||||
vsumX,
|
||||
vsumX2,
|
||||
vNminus1;
|
||||
|
||||
/* We assume the input is array of numeric */
|
||||
deconstruct_array(transarray,
|
||||
false, -1, 'i',
|
||||
&transdatums, &ndatums);
|
||||
if (ndatums != 3)
|
||||
elog(ERROR, "numeric_variance: expected 3-element numeric array");
|
||||
N = DatumGetNumeric(transdatums[0]);
|
||||
sumX = DatumGetNumeric(transdatums[1]);
|
||||
sumX2 = DatumGetNumeric(transdatums[2]);
|
||||
|
||||
if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
|
||||
/* We define VARIANCE of no values to be NULL, of 1 value to be 0 */
|
||||
/* N is zero iff no digits (cf. numeric_uminus) */
|
||||
if (N->varlen == NUMERIC_HDRSZ)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
init_var(&vN);
|
||||
set_var_from_num(N, &vN);
|
||||
|
||||
init_var(&vNminus1);
|
||||
sub_var(&vN, &const_one, &vNminus1);
|
||||
|
||||
if (cmp_var(&vNminus1, &const_zero) <= 0)
|
||||
{
|
||||
free_var(&vN);
|
||||
free_var(&vNminus1);
|
||||
PG_RETURN_NUMERIC(make_result(&const_zero));
|
||||
}
|
||||
|
||||
init_var(&vsumX);
|
||||
set_var_from_num(sumX, &vsumX);
|
||||
init_var(&vsumX2);
|
||||
set_var_from_num(sumX2, &vsumX2);
|
||||
|
||||
mul_var(&vsumX, &vsumX, &vsumX); /* now vsumX contains sumX * sumX */
|
||||
mul_var(&vN, &vsumX2, &vsumX2); /* now vsumX2 contains N * sumX2 */
|
||||
sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */
|
||||
mul_var(&vN, &vNminus1, &vNminus1); /* N * (N - 1) */
|
||||
div_var(&vsumX2, &vNminus1, &vsumX); /* variance */
|
||||
|
||||
res = make_result(&vsumX);
|
||||
|
||||
free_var(&vN);
|
||||
free_var(&vNminus1);
|
||||
free_var(&vsumX);
|
||||
free_var(&vsumX2);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
numeric_stddev(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Datum *transdatums;
|
||||
int ndatums;
|
||||
Numeric N,
|
||||
sumX,
|
||||
sumX2,
|
||||
res;
|
||||
NumericVar vN,
|
||||
vsumX,
|
||||
vsumX2,
|
||||
vNminus1;
|
||||
|
||||
/* We assume the input is array of numeric */
|
||||
deconstruct_array(transarray,
|
||||
false, -1, 'i',
|
||||
&transdatums, &ndatums);
|
||||
if (ndatums != 3)
|
||||
elog(ERROR, "numeric_stddev: expected 3-element numeric array");
|
||||
N = DatumGetNumeric(transdatums[0]);
|
||||
sumX = DatumGetNumeric(transdatums[1]);
|
||||
sumX2 = DatumGetNumeric(transdatums[2]);
|
||||
|
||||
if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
|
||||
/* We define STDDEV of no values to be NULL, of 1 value to be 0 */
|
||||
/* N is zero iff no digits (cf. numeric_uminus) */
|
||||
if (N->varlen == NUMERIC_HDRSZ)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
init_var(&vN);
|
||||
set_var_from_num(N, &vN);
|
||||
|
||||
init_var(&vNminus1);
|
||||
sub_var(&vN, &const_one, &vNminus1);
|
||||
|
||||
if (cmp_var(&vNminus1, &const_zero) <= 0)
|
||||
{
|
||||
free_var(&vN);
|
||||
free_var(&vNminus1);
|
||||
PG_RETURN_NUMERIC(make_result(&const_zero));
|
||||
}
|
||||
|
||||
init_var(&vsumX);
|
||||
set_var_from_num(sumX, &vsumX);
|
||||
init_var(&vsumX2);
|
||||
set_var_from_num(sumX2, &vsumX2);
|
||||
|
||||
mul_var(&vsumX, &vsumX, &vsumX); /* now vsumX contains sumX * sumX */
|
||||
mul_var(&vN, &vsumX2, &vsumX2); /* now vsumX2 contains N * sumX2 */
|
||||
sub_var(&vsumX2, &vsumX, &vsumX2); /* N * sumX2 - sumX * sumX */
|
||||
mul_var(&vN, &vNminus1, &vNminus1); /* N * (N - 1) */
|
||||
div_var(&vsumX2, &vNminus1, &vsumX); /* variance */
|
||||
sqrt_var(&vsumX, &vsumX); /* stddev */
|
||||
|
||||
res = make_result(&vsumX);
|
||||
|
||||
free_var(&vN);
|
||||
free_var(&vNminus1);
|
||||
free_var(&vsumX);
|
||||
free_var(&vsumX2);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SUM transition functions for integer datatypes.
|
||||
*
|
||||
* We use a Numeric accumulator to avoid overflow. Because SQL92 defines
|
||||
* the SUM() of no values to be NULL, not zero, the initial condition of
|
||||
* the transition data value needs to be NULL. This means we can't rely
|
||||
* on ExecAgg to automatically insert the first non-null data value into
|
||||
* the transition data: it doesn't know how to do the type conversion.
|
||||
* The upshot is that these routines have to be marked non-strict and
|
||||
* handle substitution of the first non-null input themselves.
|
||||
*/
|
||||
|
||||
Datum
|
||||
int2_sum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric oldsum,
|
||||
newval;
|
||||
|
||||
if (PG_ARGISNULL(0))
|
||||
{
|
||||
/* No non-null input seen so far... */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_NULL(); /* still no non-null */
|
||||
/* This is the first non-null input. */
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric,
|
||||
PG_GETARG_DATUM(1)));
|
||||
PG_RETURN_NUMERIC(newval);
|
||||
}
|
||||
|
||||
oldsum = PG_GETARG_NUMERIC(0);
|
||||
|
||||
/* Leave sum unchanged if new input is null. */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_NUMERIC(oldsum);
|
||||
|
||||
/* OK to do the addition. */
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric,
|
||||
PG_GETARG_DATUM(1)));
|
||||
|
||||
PG_RETURN_NUMERIC(numeric_add(oldsum, newval));
|
||||
}
|
||||
|
||||
Datum
|
||||
int4_sum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric oldsum,
|
||||
newval;
|
||||
|
||||
if (PG_ARGISNULL(0))
|
||||
{
|
||||
/* No non-null input seen so far... */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_NULL(); /* still no non-null */
|
||||
/* This is the first non-null input. */
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
|
||||
PG_GETARG_DATUM(1)));
|
||||
PG_RETURN_NUMERIC(newval);
|
||||
}
|
||||
|
||||
oldsum = PG_GETARG_NUMERIC(0);
|
||||
|
||||
/* Leave sum unchanged if new input is null. */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_NUMERIC(oldsum);
|
||||
|
||||
/* OK to do the addition. */
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
|
||||
PG_GETARG_DATUM(1)));
|
||||
|
||||
PG_RETURN_NUMERIC(numeric_add(oldsum, newval));
|
||||
}
|
||||
|
||||
Datum
|
||||
int8_sum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric oldsum,
|
||||
newval;
|
||||
|
||||
if (PG_ARGISNULL(0))
|
||||
{
|
||||
/* No non-null input seen so far... */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_NULL(); /* still no non-null */
|
||||
/* This is the first non-null input. */
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
|
||||
PG_GETARG_DATUM(1)));
|
||||
PG_RETURN_NUMERIC(newval);
|
||||
}
|
||||
|
||||
oldsum = PG_GETARG_NUMERIC(0);
|
||||
|
||||
/* Leave sum unchanged if new input is null. */
|
||||
if (PG_ARGISNULL(1))
|
||||
PG_RETURN_NUMERIC(oldsum);
|
||||
|
||||
/* OK to do the addition. */
|
||||
newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
|
||||
PG_GETARG_DATUM(1)));
|
||||
|
||||
PG_RETURN_NUMERIC(numeric_add(oldsum, newval));
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
*
|
||||
* Local functions follow
|
||||
@ -2574,30 +2895,33 @@ add_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
*/
|
||||
switch (cmp_abs(var1, var2))
|
||||
{
|
||||
case 0: /* ----------
|
||||
* ABS(var1) == ABS(var2)
|
||||
* result = ZERO
|
||||
* ----------
|
||||
*/
|
||||
case 0:
|
||||
/* ----------
|
||||
* ABS(var1) == ABS(var2)
|
||||
* result = ZERO
|
||||
* ----------
|
||||
*/
|
||||
zero_var(result);
|
||||
result->rscale = MAX(var1->rscale, var2->rscale);
|
||||
result->dscale = MAX(var1->dscale, var2->dscale);
|
||||
break;
|
||||
|
||||
case 1: /* ----------
|
||||
* ABS(var1) > ABS(var2)
|
||||
* result = +(ABS(var1) - ABS(var2))
|
||||
* ----------
|
||||
*/
|
||||
case 1:
|
||||
/* ----------
|
||||
* ABS(var1) > ABS(var2)
|
||||
* result = +(ABS(var1) - ABS(var2))
|
||||
* ----------
|
||||
*/
|
||||
sub_abs(var1, var2, result);
|
||||
result->sign = NUMERIC_POS;
|
||||
break;
|
||||
|
||||
case -1: /* ----------
|
||||
* ABS(var1) < ABS(var2)
|
||||
* result = -(ABS(var2) - ABS(var1))
|
||||
* ----------
|
||||
*/
|
||||
case -1:
|
||||
/* ----------
|
||||
* ABS(var1) < ABS(var2)
|
||||
* result = -(ABS(var2) - ABS(var1))
|
||||
* ----------
|
||||
*/
|
||||
sub_abs(var2, var1, result);
|
||||
result->sign = NUMERIC_NEG;
|
||||
break;
|
||||
@ -2615,30 +2939,33 @@ add_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
*/
|
||||
switch (cmp_abs(var1, var2))
|
||||
{
|
||||
case 0: /* ----------
|
||||
* ABS(var1) == ABS(var2)
|
||||
* result = ZERO
|
||||
* ----------
|
||||
*/
|
||||
case 0:
|
||||
/* ----------
|
||||
* ABS(var1) == ABS(var2)
|
||||
* result = ZERO
|
||||
* ----------
|
||||
*/
|
||||
zero_var(result);
|
||||
result->rscale = MAX(var1->rscale, var2->rscale);
|
||||
result->dscale = MAX(var1->dscale, var2->dscale);
|
||||
break;
|
||||
|
||||
case 1: /* ----------
|
||||
* ABS(var1) > ABS(var2)
|
||||
* result = -(ABS(var1) - ABS(var2))
|
||||
* ----------
|
||||
*/
|
||||
case 1:
|
||||
/* ----------
|
||||
* ABS(var1) > ABS(var2)
|
||||
* result = -(ABS(var1) - ABS(var2))
|
||||
* ----------
|
||||
*/
|
||||
sub_abs(var1, var2, result);
|
||||
result->sign = NUMERIC_NEG;
|
||||
break;
|
||||
|
||||
case -1: /* ----------
|
||||
* ABS(var1) < ABS(var2)
|
||||
* result = +(ABS(var2) - ABS(var1))
|
||||
* ----------
|
||||
*/
|
||||
case -1:
|
||||
/* ----------
|
||||
* ABS(var1) < ABS(var2)
|
||||
* result = +(ABS(var2) - ABS(var1))
|
||||
* ----------
|
||||
*/
|
||||
sub_abs(var2, var1, result);
|
||||
result->sign = NUMERIC_POS;
|
||||
break;
|
||||
@ -2693,30 +3020,33 @@ sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
*/
|
||||
switch (cmp_abs(var1, var2))
|
||||
{
|
||||
case 0: /* ----------
|
||||
* ABS(var1) == ABS(var2)
|
||||
* result = ZERO
|
||||
* ----------
|
||||
*/
|
||||
case 0:
|
||||
/* ----------
|
||||
* ABS(var1) == ABS(var2)
|
||||
* result = ZERO
|
||||
* ----------
|
||||
*/
|
||||
zero_var(result);
|
||||
result->rscale = MAX(var1->rscale, var2->rscale);
|
||||
result->dscale = MAX(var1->dscale, var2->dscale);
|
||||
break;
|
||||
|
||||
case 1: /* ----------
|
||||
* ABS(var1) > ABS(var2)
|
||||
* result = +(ABS(var1) - ABS(var2))
|
||||
* ----------
|
||||
*/
|
||||
case 1:
|
||||
/* ----------
|
||||
* ABS(var1) > ABS(var2)
|
||||
* result = +(ABS(var1) - ABS(var2))
|
||||
* ----------
|
||||
*/
|
||||
sub_abs(var1, var2, result);
|
||||
result->sign = NUMERIC_POS;
|
||||
break;
|
||||
|
||||
case -1: /* ----------
|
||||
* ABS(var1) < ABS(var2)
|
||||
* result = -(ABS(var2) - ABS(var1))
|
||||
* ----------
|
||||
*/
|
||||
case -1:
|
||||
/* ----------
|
||||
* ABS(var1) < ABS(var2)
|
||||
* result = -(ABS(var2) - ABS(var1))
|
||||
* ----------
|
||||
*/
|
||||
sub_abs(var2, var1, result);
|
||||
result->sign = NUMERIC_NEG;
|
||||
break;
|
||||
@ -2734,30 +3064,33 @@ sub_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
*/
|
||||
switch (cmp_abs(var1, var2))
|
||||
{
|
||||
case 0: /* ----------
|
||||
* ABS(var1) == ABS(var2)
|
||||
* result = ZERO
|
||||
* ----------
|
||||
*/
|
||||
case 0:
|
||||
/* ----------
|
||||
* ABS(var1) == ABS(var2)
|
||||
* result = ZERO
|
||||
* ----------
|
||||
*/
|
||||
zero_var(result);
|
||||
result->rscale = MAX(var1->rscale, var2->rscale);
|
||||
result->dscale = MAX(var1->dscale, var2->dscale);
|
||||
break;
|
||||
|
||||
case 1: /* ----------
|
||||
* ABS(var1) > ABS(var2)
|
||||
* result = -(ABS(var1) - ABS(var2))
|
||||
* ----------
|
||||
*/
|
||||
case 1:
|
||||
/* ----------
|
||||
* ABS(var1) > ABS(var2)
|
||||
* result = -(ABS(var1) - ABS(var2))
|
||||
* ----------
|
||||
*/
|
||||
sub_abs(var1, var2, result);
|
||||
result->sign = NUMERIC_NEG;
|
||||
break;
|
||||
|
||||
case -1: /* ----------
|
||||
* ABS(var1) < ABS(var2)
|
||||
* result = +(ABS(var2) - ABS(var1))
|
||||
* ----------
|
||||
*/
|
||||
case -1:
|
||||
/* ----------
|
||||
* ABS(var1) < ABS(var2)
|
||||
* result = +(ABS(var2) - ABS(var1))
|
||||
* ----------
|
||||
*/
|
||||
sub_abs(var2, var1, result);
|
||||
result->sign = NUMERIC_POS;
|
||||
break;
|
||||
@ -2817,7 +3150,7 @@ mul_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
|
||||
for (i2 = var2->ndigits - 1; i2 >= 0; i2--)
|
||||
{
|
||||
sum = sum + res_digits[i] + var1->digits[i1] * var2->digits[i2];
|
||||
sum += res_digits[i] + var1->digits[i1] * var2->digits[i2];
|
||||
res_digits[i--] = sum % 10;
|
||||
sum /= 10;
|
||||
}
|
||||
@ -3067,7 +3400,6 @@ div_var(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
|
||||
/*
|
||||
* Tidy up
|
||||
*
|
||||
*/
|
||||
digitbuf_free(dividend.buf);
|
||||
for (i = 1; i < 10; i++)
|
||||
@ -3552,6 +3884,11 @@ add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
i1,
|
||||
i2;
|
||||
int carry = 0;
|
||||
/* copy these values into local vars for speed in inner loop */
|
||||
int var1ndigits = var1->ndigits;
|
||||
int var2ndigits = var2->ndigits;
|
||||
NumericDigit *var1digits = var1->digits;
|
||||
NumericDigit *var2digits = var2->digits;
|
||||
|
||||
res_weight = MAX(var1->weight, var2->weight) + 1;
|
||||
res_rscale = MAX(var1->rscale, var2->rscale);
|
||||
@ -3569,15 +3906,25 @@ add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
{
|
||||
i1--;
|
||||
i2--;
|
||||
if (i1 >= 0 && i1 < var1->ndigits)
|
||||
carry += var1->digits[i1];
|
||||
if (i2 >= 0 && i2 < var2->ndigits)
|
||||
carry += var2->digits[i2];
|
||||
if (i1 >= 0 && i1 < var1ndigits)
|
||||
carry += var1digits[i1];
|
||||
if (i2 >= 0 && i2 < var2ndigits)
|
||||
carry += var2digits[i2];
|
||||
|
||||
res_digits[i] = carry % 10;
|
||||
carry /= 10;
|
||||
if (carry >= 10)
|
||||
{
|
||||
res_digits[i] = carry - 10;
|
||||
carry = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
res_digits[i] = carry;
|
||||
carry = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Assert(carry == 0); /* else we failed to allow for carry out */
|
||||
|
||||
while (res_ndigits > 0 && *res_digits == 0)
|
||||
{
|
||||
res_digits++;
|
||||
@ -3623,6 +3970,11 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
i1,
|
||||
i2;
|
||||
int borrow = 0;
|
||||
/* copy these values into local vars for speed in inner loop */
|
||||
int var1ndigits = var1->ndigits;
|
||||
int var2ndigits = var2->ndigits;
|
||||
NumericDigit *var1digits = var1->digits;
|
||||
NumericDigit *var2digits = var2->digits;
|
||||
|
||||
res_weight = var1->weight;
|
||||
res_rscale = MAX(var1->rscale, var2->rscale);
|
||||
@ -3640,10 +3992,10 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
{
|
||||
i1--;
|
||||
i2--;
|
||||
if (i1 >= 0 && i1 < var1->ndigits)
|
||||
borrow += var1->digits[i1];
|
||||
if (i2 >= 0 && i2 < var2->ndigits)
|
||||
borrow -= var2->digits[i2];
|
||||
if (i1 >= 0 && i1 < var1ndigits)
|
||||
borrow += var1digits[i1];
|
||||
if (i2 >= 0 && i2 < var2ndigits)
|
||||
borrow -= var2digits[i2];
|
||||
|
||||
if (borrow < 0)
|
||||
{
|
||||
@ -3657,6 +4009,8 @@ sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
|
||||
}
|
||||
}
|
||||
|
||||
Assert(borrow == 0); /* else caller gave us var1 < var2 */
|
||||
|
||||
while (res_ndigits > 0 && *res_digits == 0)
|
||||
{
|
||||
res_digits++;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.33 2000/07/12 22:59:09 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.34 2000/07/17 03:05:18 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -29,6 +29,7 @@
|
||||
#include "access/hash.h"
|
||||
#include "access/xact.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
|
||||
@ -882,10 +883,6 @@ overlaps_timestamp(PG_FUNCTION_ARGS)
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* "Arithmetic" operators on date/times.
|
||||
* timestamp_foo returns foo as an object (pointer) that
|
||||
* can be passed between languages.
|
||||
* timestamp_xx is an internal routine which returns the
|
||||
* actual value.
|
||||
*---------------------------------------------------------*/
|
||||
|
||||
Datum
|
||||
@ -1150,7 +1147,6 @@ interval_larger(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_INTERVAL_P(result);
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
interval_pl(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@ -1232,6 +1228,90 @@ interval_div(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_INTERVAL_P(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* interval_accum and interval_avg implement the AVG(interval) aggregate.
|
||||
*
|
||||
* The transition datatype for this aggregate is a 2-element array of
|
||||
* intervals, where the first is the running sum and the second contains
|
||||
* the number of values so far in its 'time' field. This is a bit ugly
|
||||
* but it beats inventing a specialized datatype for the purpose.
|
||||
*/
|
||||
|
||||
Datum
|
||||
interval_accum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Interval *newval = PG_GETARG_INTERVAL_P(1);
|
||||
Datum *transdatums;
|
||||
int ndatums;
|
||||
Interval sumX,
|
||||
N;
|
||||
Interval *newsum;
|
||||
ArrayType *result;
|
||||
|
||||
/* We assume the input is array of interval */
|
||||
deconstruct_array(transarray,
|
||||
false, 12, 'd',
|
||||
&transdatums, &ndatums);
|
||||
if (ndatums != 2)
|
||||
elog(ERROR, "interval_accum: expected 2-element interval array");
|
||||
/*
|
||||
* XXX memcpy, instead of just extracting a pointer, to work around
|
||||
* buggy array code: it won't ensure proper alignment of Interval
|
||||
* objects on machines where double requires 8-byte alignment.
|
||||
* That should be fixed, but in the meantime...
|
||||
*/
|
||||
memcpy(&sumX, DatumGetIntervalP(transdatums[0]), sizeof(Interval));
|
||||
memcpy(&N, DatumGetIntervalP(transdatums[1]), sizeof(Interval));
|
||||
|
||||
newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
|
||||
IntervalPGetDatum(&sumX),
|
||||
IntervalPGetDatum(newval)));
|
||||
N.time += 1;
|
||||
|
||||
transdatums[0] = IntervalPGetDatum(newsum);
|
||||
transdatums[1] = IntervalPGetDatum(&N);
|
||||
|
||||
result = construct_array(transdatums, 2,
|
||||
false, 12, 'd');
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(result);
|
||||
}
|
||||
|
||||
Datum
|
||||
interval_avg(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
|
||||
Datum *transdatums;
|
||||
int ndatums;
|
||||
Interval sumX,
|
||||
N;
|
||||
|
||||
/* We assume the input is array of interval */
|
||||
deconstruct_array(transarray,
|
||||
false, 12, 'd',
|
||||
&transdatums, &ndatums);
|
||||
if (ndatums != 2)
|
||||
elog(ERROR, "interval_avg: expected 2-element interval array");
|
||||
/*
|
||||
* XXX memcpy, instead of just extracting a pointer, to work around
|
||||
* buggy array code: it won't ensure proper alignment of Interval
|
||||
* objects on machines where double requires 8-byte alignment.
|
||||
* That should be fixed, but in the meantime...
|
||||
*/
|
||||
memcpy(&sumX, DatumGetIntervalP(transdatums[0]), sizeof(Interval));
|
||||
memcpy(&N, DatumGetIntervalP(transdatums[1]), sizeof(Interval));
|
||||
|
||||
/* SQL92 defines AVG of no values to be NULL */
|
||||
if (N.time == 0)
|
||||
PG_RETURN_NULL();
|
||||
|
||||
return DirectFunctionCall2(interval_div,
|
||||
IntervalPGetDatum(&sumX),
|
||||
Float8GetDatum(N.time));
|
||||
}
|
||||
|
||||
|
||||
/* timestamp_age()
|
||||
* Calculate time difference while retaining year/month fields.
|
||||
* Note that this does not result in an accurate absolute time span
|
||||
|
@ -22,7 +22,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.158 2000/07/11 13:07:17 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.159 2000/07/17 03:05:20 tgl Exp $
|
||||
*
|
||||
* Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
|
||||
*
|
||||
@ -1421,22 +1421,16 @@ clearAggInfo(AggInfo *agginfo, int numArgs)
|
||||
free(agginfo[i].oid);
|
||||
if (agginfo[i].aggname)
|
||||
free(agginfo[i].aggname);
|
||||
if (agginfo[i].aggtransfn1)
|
||||
free(agginfo[i].aggtransfn1);
|
||||
if (agginfo[i].aggtransfn2)
|
||||
free(agginfo[i].aggtransfn2);
|
||||
if (agginfo[i].aggtransfn)
|
||||
free(agginfo[i].aggtransfn);
|
||||
if (agginfo[i].aggfinalfn)
|
||||
free(agginfo[i].aggfinalfn);
|
||||
if (agginfo[i].aggtranstype1)
|
||||
free(agginfo[i].aggtranstype1);
|
||||
if (agginfo[i].aggtranstype)
|
||||
free(agginfo[i].aggtranstype);
|
||||
if (agginfo[i].aggbasetype)
|
||||
free(agginfo[i].aggbasetype);
|
||||
if (agginfo[i].aggtranstype2)
|
||||
free(agginfo[i].aggtranstype2);
|
||||
if (agginfo[i].agginitval1)
|
||||
free(agginfo[i].agginitval1);
|
||||
if (agginfo[i].agginitval2)
|
||||
free(agginfo[i].agginitval2);
|
||||
if (agginfo[i].agginitval)
|
||||
free(agginfo[i].agginitval);
|
||||
if (agginfo[i].usename)
|
||||
free(agginfo[i].usename);
|
||||
}
|
||||
@ -1463,22 +1457,19 @@ getAggregates(int *numAggs)
|
||||
|
||||
int i_oid;
|
||||
int i_aggname;
|
||||
int i_aggtransfn1;
|
||||
int i_aggtransfn2;
|
||||
int i_aggtransfn;
|
||||
int i_aggfinalfn;
|
||||
int i_aggtranstype1;
|
||||
int i_aggtranstype;
|
||||
int i_aggbasetype;
|
||||
int i_aggtranstype2;
|
||||
int i_agginitval1;
|
||||
int i_agginitval2;
|
||||
int i_agginitval;
|
||||
int i_usename;
|
||||
|
||||
/* find all user-defined aggregates */
|
||||
|
||||
appendPQExpBuffer(query,
|
||||
"SELECT pg_aggregate.oid, aggname, aggtransfn1, aggtransfn2, "
|
||||
"aggfinalfn, aggtranstype1, aggbasetype, aggtranstype2, "
|
||||
"agginitval1, agginitval2, usename from pg_aggregate, pg_user "
|
||||
"SELECT pg_aggregate.oid, aggname, aggtransfn, "
|
||||
"aggfinalfn, aggtranstype, aggbasetype, "
|
||||
"agginitval, usename from pg_aggregate, pg_user "
|
||||
"where aggowner = usesysid");
|
||||
|
||||
res = PQexec(g_conn, query->data);
|
||||
@ -1497,28 +1488,22 @@ getAggregates(int *numAggs)
|
||||
|
||||
i_oid = PQfnumber(res, "oid");
|
||||
i_aggname = PQfnumber(res, "aggname");
|
||||
i_aggtransfn1 = PQfnumber(res, "aggtransfn1");
|
||||
i_aggtransfn2 = PQfnumber(res, "aggtransfn2");
|
||||
i_aggtransfn = PQfnumber(res, "aggtransfn");
|
||||
i_aggfinalfn = PQfnumber(res, "aggfinalfn");
|
||||
i_aggtranstype1 = PQfnumber(res, "aggtranstype1");
|
||||
i_aggtranstype = PQfnumber(res, "aggtranstype");
|
||||
i_aggbasetype = PQfnumber(res, "aggbasetype");
|
||||
i_aggtranstype2 = PQfnumber(res, "aggtranstype2");
|
||||
i_agginitval1 = PQfnumber(res, "agginitval1");
|
||||
i_agginitval2 = PQfnumber(res, "agginitval2");
|
||||
i_agginitval = PQfnumber(res, "agginitval");
|
||||
i_usename = PQfnumber(res, "usename");
|
||||
|
||||
for (i = 0; i < ntups; i++)
|
||||
{
|
||||
agginfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
|
||||
agginfo[i].aggname = strdup(PQgetvalue(res, i, i_aggname));
|
||||
agginfo[i].aggtransfn1 = strdup(PQgetvalue(res, i, i_aggtransfn1));
|
||||
agginfo[i].aggtransfn2 = strdup(PQgetvalue(res, i, i_aggtransfn2));
|
||||
agginfo[i].aggtransfn = strdup(PQgetvalue(res, i, i_aggtransfn));
|
||||
agginfo[i].aggfinalfn = strdup(PQgetvalue(res, i, i_aggfinalfn));
|
||||
agginfo[i].aggtranstype1 = strdup(PQgetvalue(res, i, i_aggtranstype1));
|
||||
agginfo[i].aggtranstype = strdup(PQgetvalue(res, i, i_aggtranstype));
|
||||
agginfo[i].aggbasetype = strdup(PQgetvalue(res, i, i_aggbasetype));
|
||||
agginfo[i].aggtranstype2 = strdup(PQgetvalue(res, i, i_aggtranstype2));
|
||||
agginfo[i].agginitval1 = strdup(PQgetvalue(res, i, i_agginitval1));
|
||||
agginfo[i].agginitval2 = strdup(PQgetvalue(res, i, i_agginitval2));
|
||||
agginfo[i].agginitval = strdup(PQgetvalue(res, i, i_agginitval));
|
||||
agginfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
|
||||
}
|
||||
|
||||
@ -2902,69 +2887,32 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs,
|
||||
PQExpBuffer q = createPQExpBuffer();
|
||||
PQExpBuffer delq = createPQExpBuffer();
|
||||
PQExpBuffer aggSig = createPQExpBuffer();
|
||||
PQExpBuffer sfunc1 = createPQExpBuffer();
|
||||
PQExpBuffer sfunc2 = createPQExpBuffer();
|
||||
PQExpBuffer basetype = createPQExpBuffer();
|
||||
PQExpBuffer finalfunc = createPQExpBuffer();
|
||||
char comma1[2],
|
||||
comma2[2];
|
||||
PQExpBuffer details = createPQExpBuffer();
|
||||
|
||||
for (i = 0; i < numAggs; i++)
|
||||
{
|
||||
|
||||
resetPQExpBuffer(sfunc1);
|
||||
resetPQExpBuffer(sfunc2);
|
||||
resetPQExpBuffer(basetype);
|
||||
resetPQExpBuffer(finalfunc);
|
||||
resetPQExpBuffer(details);
|
||||
|
||||
/* skip all the builtin oids */
|
||||
if (atoi(agginfo[i].oid) < g_last_builtin_oid)
|
||||
continue;
|
||||
|
||||
appendPQExpBuffer(basetype,
|
||||
appendPQExpBuffer(details,
|
||||
"BASETYPE = %s, ",
|
||||
fmtId(findTypeByOid(tinfo, numTypes, agginfo[i].aggbasetype), false));
|
||||
|
||||
if (!(strcmp(agginfo[i].aggtransfn1, "-") == 0))
|
||||
{
|
||||
appendPQExpBuffer(sfunc1,
|
||||
"SFUNC1 = %s, STYPE1 = %s",
|
||||
agginfo[i].aggtransfn1,
|
||||
fmtId(findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype1), false));
|
||||
if (agginfo[i].agginitval1)
|
||||
appendPQExpBuffer(sfunc1, ", INITCOND1 = '%s'",
|
||||
agginfo[i].agginitval1);
|
||||
appendPQExpBuffer(details,
|
||||
"SFUNC = %s, STYPE = %s",
|
||||
agginfo[i].aggtransfn,
|
||||
fmtId(findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype), false));
|
||||
|
||||
}
|
||||
|
||||
if (!(strcmp(agginfo[i].aggtransfn2, "-") == 0))
|
||||
{
|
||||
appendPQExpBuffer(sfunc2,
|
||||
"SFUNC2 = %s, STYPE2 = %s",
|
||||
agginfo[i].aggtransfn2,
|
||||
fmtId(findTypeByOid(tinfo, numTypes, agginfo[i].aggtranstype2), false));
|
||||
if (agginfo[i].agginitval2)
|
||||
appendPQExpBuffer(sfunc2, ", INITCOND2 = '%s'",
|
||||
agginfo[i].agginitval2);
|
||||
}
|
||||
if (agginfo[i].agginitval)
|
||||
appendPQExpBuffer(details, ", INITCOND = '%s'",
|
||||
agginfo[i].agginitval);
|
||||
|
||||
if (!(strcmp(agginfo[i].aggfinalfn, "-") == 0))
|
||||
appendPQExpBuffer(finalfunc, "FINALFUNC = %s", agginfo[i].aggfinalfn);
|
||||
if (sfunc1->data[0] != '\0' && sfunc2->data[0] != '\0')
|
||||
{
|
||||
comma1[0] = ',';
|
||||
comma1[1] = '\0';
|
||||
}
|
||||
else
|
||||
comma1[0] = '\0';
|
||||
|
||||
if (finalfunc->data[0] != '\0' && (sfunc1->data[0] != '\0' || sfunc2->data[0] != '\0'))
|
||||
{
|
||||
comma2[0] = ',';
|
||||
comma2[1] = '\0';
|
||||
}
|
||||
else
|
||||
comma2[0] = '\0';
|
||||
appendPQExpBuffer(details, ", FINALFUNC = %s",
|
||||
agginfo[i].aggfinalfn);
|
||||
|
||||
resetPQExpBuffer(aggSig);
|
||||
appendPQExpBuffer(aggSig, "%s %s", agginfo[i].aggname,
|
||||
@ -2974,14 +2922,9 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs,
|
||||
appendPQExpBuffer(delq, "DROP AGGREGATE %s;\n", aggSig->data);
|
||||
|
||||
resetPQExpBuffer(q);
|
||||
appendPQExpBuffer(q, "CREATE AGGREGATE %s ( %s %s%s %s%s %s );\n",
|
||||
appendPQExpBuffer(q, "CREATE AGGREGATE %s ( %s );\n",
|
||||
agginfo[i].aggname,
|
||||
basetype->data,
|
||||
sfunc1->data,
|
||||
comma1,
|
||||
sfunc2->data,
|
||||
comma2,
|
||||
finalfunc->data);
|
||||
details->data);
|
||||
|
||||
ArchiveEntry(fout, agginfo[i].oid, aggSig->data, "AGGREGATE", NULL,
|
||||
q->data, delq->data, agginfo[i].usename, NULL, NULL);
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pg_dump.h,v 1.49 2000/07/04 14:25:28 momjian Exp $
|
||||
* $Id: pg_dump.h,v 1.50 2000/07/17 03:05:20 tgl Exp $
|
||||
*
|
||||
* Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2
|
||||
*
|
||||
@ -133,14 +133,11 @@ typedef struct _aggInfo
|
||||
{
|
||||
char *oid;
|
||||
char *aggname;
|
||||
char *aggtransfn1;
|
||||
char *aggtransfn2;
|
||||
char *aggtransfn;
|
||||
char *aggfinalfn;
|
||||
char *aggtranstype1;
|
||||
char *aggtranstype;
|
||||
char *aggbasetype;
|
||||
char *aggtranstype2;
|
||||
char *agginitval1;
|
||||
char *agginitval2;
|
||||
char *agginitval;
|
||||
char *usename;
|
||||
} AggInfo;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: c.h,v 1.78 2000/07/12 22:59:12 petere Exp $
|
||||
* $Id: c.h,v 1.79 2000/07/17 03:05:20 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -587,6 +587,25 @@ extern Datum Float8GetDatum(float8 X);
|
||||
|
||||
#define Float64GetDatum(X) PointerGetDatum(X)
|
||||
|
||||
/*
|
||||
* Int64GetDatumFast
|
||||
* Float4GetDatumFast
|
||||
* Float8GetDatumFast
|
||||
*
|
||||
* These macros are intended to allow writing code that does not depend on
|
||||
* whether int64, float4, float8 are pass-by-reference types, while not
|
||||
* sacrificing performance when they are. The argument must be a variable
|
||||
* that will exist and have the same value for as long as the Datum is needed.
|
||||
* In the pass-by-ref case, the address of the variable is taken to use as
|
||||
* the Datum. In the pass-by-val case, these will be the same as the non-Fast
|
||||
* macros.
|
||||
*/
|
||||
|
||||
#define Int64GetDatumFast(X) PointerGetDatum(&(X))
|
||||
#define Float4GetDatumFast(X) PointerGetDatum(&(X))
|
||||
#define Float8GetDatumFast(X) PointerGetDatum(&(X))
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Section 5: IsValid macros for system types
|
||||
* ----------------------------------------------------------------
|
||||
|
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: catversion.h,v 1.36 2000/07/07 19:24:41 petere Exp $
|
||||
* $Id: catversion.h,v 1.37 2000/07/17 03:05:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200007071
|
||||
#define CATALOG_VERSION_NO 200007161
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pg_aggregate.h,v 1.26 2000/05/30 04:24:55 tgl Exp $
|
||||
* $Id: pg_aggregate.h,v 1.27 2000/07/17 03:05:23 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* the genbki.sh script reads this file and generates .bki
|
||||
@ -32,30 +32,25 @@
|
||||
* cpp turns this into typedef struct FormData_pg_aggregate
|
||||
*
|
||||
* aggname name of the aggregate
|
||||
* aggtransfn1 transition function 1
|
||||
* aggtransfn2 transition function 2
|
||||
* aggowner owner (creator) of the aggregate
|
||||
* aggtransfn transition function
|
||||
* aggfinalfn final function
|
||||
* aggbasetype type of data on which aggregate operates
|
||||
* aggtranstype1 output types for transition func 1
|
||||
* aggtranstype2 output types for transition func 2
|
||||
* aggfinaltype output type for final function
|
||||
* agginitval1 initial aggregate value
|
||||
* agginitval2 initial value for transition state 2
|
||||
* aggtranstype type of aggregate's transition (state) data
|
||||
* aggfinaltype type of aggregate's final result
|
||||
* agginitval initial value for transition state
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
CATALOG(pg_aggregate)
|
||||
{
|
||||
NameData aggname;
|
||||
int4 aggowner;
|
||||
regproc aggtransfn1;
|
||||
regproc aggtransfn2;
|
||||
regproc aggtransfn;
|
||||
regproc aggfinalfn;
|
||||
Oid aggbasetype;
|
||||
Oid aggtranstype1;
|
||||
Oid aggtranstype2;
|
||||
Oid aggtranstype;
|
||||
Oid aggfinaltype;
|
||||
text agginitval1; /* VARIABLE LENGTH FIELD */
|
||||
text agginitval2; /* VARIABLE LENGTH FIELD */
|
||||
text agginitval; /* VARIABLE LENGTH FIELD */
|
||||
} FormData_pg_aggregate;
|
||||
|
||||
/* ----------------
|
||||
@ -70,18 +65,15 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
#define Natts_pg_aggregate 11
|
||||
#define Natts_pg_aggregate 8
|
||||
#define Anum_pg_aggregate_aggname 1
|
||||
#define Anum_pg_aggregate_aggowner 2
|
||||
#define Anum_pg_aggregate_aggtransfn1 3
|
||||
#define Anum_pg_aggregate_aggtransfn2 4
|
||||
#define Anum_pg_aggregate_aggfinalfn 5
|
||||
#define Anum_pg_aggregate_aggbasetype 6
|
||||
#define Anum_pg_aggregate_aggtranstype1 7
|
||||
#define Anum_pg_aggregate_aggtranstype2 8
|
||||
#define Anum_pg_aggregate_aggfinaltype 9
|
||||
#define Anum_pg_aggregate_agginitval1 10
|
||||
#define Anum_pg_aggregate_agginitval2 11
|
||||
#define Anum_pg_aggregate_aggtransfn 3
|
||||
#define Anum_pg_aggregate_aggfinalfn 4
|
||||
#define Anum_pg_aggregate_aggbasetype 5
|
||||
#define Anum_pg_aggregate_aggtranstype 6
|
||||
#define Anum_pg_aggregate_aggfinaltype 7
|
||||
#define Anum_pg_aggregate_agginitval 8
|
||||
|
||||
|
||||
/* ----------------
|
||||
@ -89,70 +81,84 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
|
||||
* ---------------
|
||||
*/
|
||||
|
||||
DATA(insert OID = 0 ( avg PGUID int8pl int4inc int84div 20 20 23 20 _null_ 0 ));
|
||||
DATA(insert OID = 0 ( avg PGUID int4pl int4inc int4div 23 23 23 23 _null_ 0 ));
|
||||
DATA(insert OID = 0 ( avg PGUID int2pl int2inc int2div 21 21 21 21 _null_ 0 ));
|
||||
DATA(insert OID = 0 ( avg PGUID float4pl float4inc float4div 700 700 700 700 _null_ 0.0 ));
|
||||
DATA(insert OID = 0 ( avg PGUID float8pl float8inc float8div 701 701 701 701 _null_ 0.0 ));
|
||||
DATA(insert OID = 0 ( avg PGUID cash_pl float8inc cash_div_flt8 790 790 701 790 _null_ 0.0 ));
|
||||
DATA(insert OID = 0 ( avg PGUID interval_pl float8inc interval_div 1186 1186 701 1186 _null_ 0.0 ));
|
||||
DATA(insert OID = 0 ( avg PGUID numeric_add numeric_inc numeric_div 1700 1700 1700 1700 _null_ 0 ));
|
||||
DATA(insert OID = 0 ( avg PGUID int8_accum numeric_avg 20 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( avg PGUID int4_accum numeric_avg 23 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( avg PGUID int2_accum numeric_avg 21 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( avg PGUID numeric_accum numeric_avg 1700 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( avg PGUID float4_accum float8_avg 700 1022 701 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( avg PGUID float8_accum float8_avg 701 1022 701 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( avg PGUID interval_accum interval_avg 1186 1187 1186 "{0,0}" ));
|
||||
|
||||
DATA(insert OID = 0 ( sum PGUID int8pl - - 20 20 0 20 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID int4pl - - 23 23 0 23 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID int2pl - - 21 21 0 21 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID float4pl - - 700 700 0 700 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID float8pl - - 701 701 0 701 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID cash_pl - - 790 790 0 790 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID interval_pl - - 1186 1186 0 1186 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID numeric_add - - 1700 1700 0 1700 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID int8_sum - 20 1700 1700 _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID int4_sum - 23 1700 1700 _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID int2_sum - 21 1700 1700 _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID float4pl - 700 700 700 _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID float8pl - 701 701 701 _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID cash_pl - 790 790 790 _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID interval_pl - 1186 1186 1186 _null_ ));
|
||||
DATA(insert OID = 0 ( sum PGUID numeric_add - 1700 1700 1700 _null_ ));
|
||||
|
||||
DATA(insert OID = 0 ( max PGUID int8larger - - 20 20 0 20 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID int4larger - - 23 23 0 23 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID int2larger - - 21 21 0 21 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID float4larger - - 700 700 0 700 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID float8larger - - 701 701 0 701 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID int4larger - - 702 702 0 702 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID date_larger - - 1082 1082 0 1082 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID time_larger - - 1083 1083 0 1083 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID timetz_larger - - 1266 1266 0 1266 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID cashlarger - - 790 790 0 790 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID timestamp_larger - - 1184 1184 0 1184 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID interval_larger - - 1186 1186 0 1186 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID text_larger - - 25 25 0 25 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID numeric_larger - - 1700 1700 0 1700 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID int8larger - 20 20 20 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID int4larger - 23 23 23 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID int2larger - 21 21 21 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID float4larger - 700 700 700 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID float8larger - 701 701 701 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID int4larger - 702 702 702 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID date_larger - 1082 1082 1082 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID time_larger - 1083 1083 1083 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID timetz_larger - 1266 1266 1266 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID cashlarger - 790 790 790 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID timestamp_larger - 1184 1184 1184 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID interval_larger - 1186 1186 1186 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID text_larger - 25 25 25 _null_ ));
|
||||
DATA(insert OID = 0 ( max PGUID numeric_larger - 1700 1700 1700 _null_ ));
|
||||
|
||||
DATA(insert OID = 0 ( min PGUID int8smaller - - 20 20 0 20 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID int4smaller - - 23 23 0 23 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID int2smaller - - 21 21 0 21 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID float4smaller - - 700 700 0 700 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID float8smaller - - 701 701 0 701 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID int4smaller - - 702 702 0 702 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID date_smaller - - 1082 1082 0 1082 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID time_smaller - - 1083 1083 0 1083 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID timetz_smaller - - 1266 1266 0 1266 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID cashsmaller - - 790 790 0 790 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID timestamp_smaller - - 1184 1184 0 1184 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID interval_smaller - - 1186 1186 0 1186 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID text_smaller - - 25 25 0 25 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID numeric_smaller - - 1700 1700 0 1700 _null_ _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID int8smaller - 20 20 20 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID int4smaller - 23 23 23 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID int2smaller - 21 21 21 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID float4smaller - 700 700 700 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID float8smaller - 701 701 701 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID int4smaller - 702 702 702 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID date_smaller - 1082 1082 1082 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID time_smaller - 1083 1083 1083 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID timetz_smaller - 1266 1266 1266 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID cashsmaller - 790 790 790 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID timestamp_smaller - 1184 1184 1184 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID interval_smaller - 1186 1186 1186 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID text_smaller - 25 25 25 _null_ ));
|
||||
DATA(insert OID = 0 ( min PGUID numeric_smaller - 1700 1700 1700 _null_ ));
|
||||
|
||||
DATA(insert OID = 0 ( count PGUID - int4inc - 0 0 23 23 _null_ 0 ));
|
||||
/*
|
||||
* Using int4inc for count() is cheating a little, since it really only
|
||||
* takes 1 parameter not 2, but nodeAgg.c won't complain ...
|
||||
*/
|
||||
DATA(insert OID = 0 ( count PGUID int4inc - 0 23 23 0 ));
|
||||
|
||||
DATA(insert OID = 0 ( variance PGUID int8_accum numeric_variance 20 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( variance PGUID int4_accum numeric_variance 23 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( variance PGUID int2_accum numeric_variance 21 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( variance PGUID float4_accum float8_variance 700 1022 701 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( variance PGUID float8_accum float8_variance 701 1022 701 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( variance PGUID numeric_accum numeric_variance 1700 1231 1700 "{0,0,0}" ));
|
||||
|
||||
DATA(insert OID = 0 ( stddev PGUID int8_accum numeric_stddev 20 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( stddev PGUID int4_accum numeric_stddev 23 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( stddev PGUID int2_accum numeric_stddev 21 1231 1700 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( stddev PGUID float4_accum float8_stddev 700 1022 701 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( stddev PGUID float8_accum float8_stddev 701 1022 701 "{0,0,0}" ));
|
||||
DATA(insert OID = 0 ( stddev PGUID numeric_accum numeric_stddev 1700 1231 1700 "{0,0,0}" ));
|
||||
|
||||
/*
|
||||
* prototypes for functions in pg_aggregate.c
|
||||
*/
|
||||
extern void AggregateCreate(char *aggName,
|
||||
char *aggtransfn1Name,
|
||||
char *aggtransfn2Name,
|
||||
char *aggtransfnName,
|
||||
char *aggfinalfnName,
|
||||
char *aggbasetypeName,
|
||||
char *aggtransfn1typeName,
|
||||
char *aggtransfn2typeName,
|
||||
char *agginitval1,
|
||||
char *agginitval2);
|
||||
char *aggtranstypeName,
|
||||
char *agginitval);
|
||||
|
||||
extern Datum AggNameGetInitVal(char *aggName, Oid basetype,
|
||||
int xfuncno, bool *isNull);
|
||||
bool *isNull);
|
||||
|
||||
#endif /* PG_AGGREGATE_H */
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pg_operator.h,v 1.76 2000/06/05 07:28:59 tgl Exp $
|
||||
* $Id: pg_operator.h,v 1.77 2000/07/17 03:05:23 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* the genbki.sh script reads this file and generates .bki
|
||||
@ -323,12 +323,12 @@ DATA(insert OID = 636 ( "-" PGUID 0 b t f 18 18 18 0 0 0 0 charmi - - ))
|
||||
DATA(insert OID = 637 ( "*" PGUID 0 b t f 18 18 18 0 0 0 0 charmul - - ));
|
||||
DATA(insert OID = 638 ( "/" PGUID 0 b t f 18 18 18 0 0 0 0 chardiv - - ));
|
||||
|
||||
DATA(insert OID = 639 ( "~" PGUID 0 b t f 19 25 16 0 640 0 0 nameregexeq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 639 ( "~" PGUID 0 b t f 19 25 16 0 640 0 0 nameregexeq regexeqsel regexeqjoinsel ));
|
||||
#define OID_NAME_REGEXEQ_OP 639
|
||||
DATA(insert OID = 640 ( "!~" PGUID 0 b t f 19 25 16 0 639 0 0 nameregexne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 641 ( "~" PGUID 0 b t f 25 25 16 0 642 0 0 textregexeq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 640 ( "!~" PGUID 0 b t f 19 25 16 0 639 0 0 nameregexne regexnesel regexnejoinsel ));
|
||||
DATA(insert OID = 641 ( "~" PGUID 0 b t f 25 25 16 0 642 0 0 textregexeq regexeqsel regexeqjoinsel ));
|
||||
#define OID_TEXT_REGEXEQ_OP 641
|
||||
DATA(insert OID = 642 ( "!~" PGUID 0 b t f 25 25 16 0 641 0 0 textregexne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 642 ( "!~" PGUID 0 b t f 25 25 16 0 641 0 0 textregexne regexnesel regexnejoinsel ));
|
||||
DATA(insert OID = 643 ( "<>" PGUID 0 b t f 19 19 16 643 93 0 0 namene neqsel neqjoinsel ));
|
||||
DATA(insert OID = 654 ( "||" PGUID 0 b t f 25 25 25 0 0 0 0 textcat - - ));
|
||||
|
||||
@ -449,9 +449,9 @@ DATA(insert OID = 974 ( "||" PGUID 0 b t f 1042 1042 1042 0 0 0 0 textc
|
||||
DATA(insert OID = 979 ( "||" PGUID 0 b t f 1043 1043 1043 0 0 0 0 textcat - - ));
|
||||
|
||||
DATA(insert OID = 1054 ( "=" PGUID 0 b t f 1042 1042 16 1054 1057 1058 1058 bpchareq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1055 ( "~" PGUID 0 b t f 1042 25 16 0 1056 0 0 textregexeq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1055 ( "~" PGUID 0 b t f 1042 25 16 0 1056 0 0 textregexeq regexeqsel regexeqjoinsel ));
|
||||
#define OID_BPCHAR_REGEXEQ_OP 1055
|
||||
DATA(insert OID = 1056 ( "!~" PGUID 0 b t f 1042 25 16 0 1055 0 0 textregexne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1056 ( "!~" PGUID 0 b t f 1042 25 16 0 1055 0 0 textregexne regexnesel regexnejoinsel ));
|
||||
DATA(insert OID = 1057 ( "<>" PGUID 0 b t f 1042 1042 16 1057 1054 0 0 bpcharne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1058 ( "<" PGUID 0 b t f 1042 1042 16 1060 1061 0 0 bpcharlt scalarltsel scalarltjoinsel ));
|
||||
DATA(insert OID = 1059 ( "<=" PGUID 0 b t f 1042 1042 16 1061 1060 0 0 bpcharle scalarltsel scalarltjoinsel ));
|
||||
@ -459,9 +459,9 @@ DATA(insert OID = 1060 ( ">" PGUID 0 b t f 1042 1042 16 1058 1059 0 0 bpcha
|
||||
DATA(insert OID = 1061 ( ">=" PGUID 0 b t f 1042 1042 16 1059 1058 0 0 bpcharge scalargtsel scalargtjoinsel ));
|
||||
|
||||
DATA(insert OID = 1062 ( "=" PGUID 0 b t t 1043 1043 16 1062 1065 1066 1066 varchareq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1063 ( "~" PGUID 0 b t f 1043 25 16 0 1064 0 0 textregexeq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1063 ( "~" PGUID 0 b t f 1043 25 16 0 1064 0 0 textregexeq regexeqsel regexeqjoinsel ));
|
||||
#define OID_VARCHAR_REGEXEQ_OP 1063
|
||||
DATA(insert OID = 1064 ( "!~" PGUID 0 b t f 1043 25 16 0 1063 0 0 textregexne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1064 ( "!~" PGUID 0 b t f 1043 25 16 0 1063 0 0 textregexne regexnesel regexnejoinsel ));
|
||||
DATA(insert OID = 1065 ( "<>" PGUID 0 b t f 1043 1043 16 1065 1062 0 0 varcharne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1066 ( "<" PGUID 0 b t f 1043 1043 16 1068 1069 0 0 varcharlt scalarltsel scalarltjoinsel ));
|
||||
DATA(insert OID = 1067 ( "<=" PGUID 0 b t f 1043 1043 16 1069 1068 0 0 varcharle scalarltsel scalarltjoinsel ));
|
||||
@ -527,32 +527,32 @@ DATA(insert OID = 1158 ( "!" PGUID 0 r t f 21 0 23 0 0 0 0 int2fac - - ));
|
||||
DATA(insert OID = 1175 ( "!!" PGUID 0 l t f 0 21 23 0 0 0 0 int2fac - - ));
|
||||
|
||||
/* LIKE hacks by Keith Parks. */
|
||||
DATA(insert OID = 1207 ( "~~" PGUID 0 b t f 19 25 16 0 1208 0 0 namelike eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1207 ( "~~" PGUID 0 b t f 19 25 16 0 1208 0 0 namelike likesel likejoinsel ));
|
||||
#define OID_NAME_LIKE_OP 1207
|
||||
DATA(insert OID = 1208 ( "!~~" PGUID 0 b t f 19 25 16 0 1207 0 0 namenlike neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1209 ( "~~" PGUID 0 b t f 25 25 16 0 1210 0 0 textlike eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1208 ( "!~~" PGUID 0 b t f 19 25 16 0 1207 0 0 namenlike nlikesel nlikejoinsel ));
|
||||
DATA(insert OID = 1209 ( "~~" PGUID 0 b t f 25 25 16 0 1210 0 0 textlike likesel likejoinsel ));
|
||||
#define OID_TEXT_LIKE_OP 1209
|
||||
DATA(insert OID = 1210 ( "!~~" PGUID 0 b t f 25 25 16 0 1209 0 0 textnlike neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1211 ( "~~" PGUID 0 b t f 1042 25 16 0 1212 0 0 textlike eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1210 ( "!~~" PGUID 0 b t f 25 25 16 0 1209 0 0 textnlike nlikesel nlikejoinsel ));
|
||||
DATA(insert OID = 1211 ( "~~" PGUID 0 b t f 1042 25 16 0 1212 0 0 textlike likesel likejoinsel ));
|
||||
#define OID_BPCHAR_LIKE_OP 1211
|
||||
DATA(insert OID = 1212 ( "!~~" PGUID 0 b t f 1042 25 16 0 1211 0 0 textnlike neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1213 ( "~~" PGUID 0 b t f 1043 25 16 0 1214 0 0 textlike eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1212 ( "!~~" PGUID 0 b t f 1042 25 16 0 1211 0 0 textnlike nlikesel nlikejoinsel ));
|
||||
DATA(insert OID = 1213 ( "~~" PGUID 0 b t f 1043 25 16 0 1214 0 0 textlike likesel likejoinsel ));
|
||||
#define OID_VARCHAR_LIKE_OP 1213
|
||||
DATA(insert OID = 1214 ( "!~~" PGUID 0 b t f 1043 25 16 0 1213 0 0 textnlike neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1214 ( "!~~" PGUID 0 b t f 1043 25 16 0 1213 0 0 textnlike nlikesel nlikejoinsel ));
|
||||
|
||||
/* case-insensitive LIKE hacks */
|
||||
DATA(insert OID = 1226 ( "~*" PGUID 0 b t f 19 25 16 0 1227 0 0 nameicregexeq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1226 ( "~*" PGUID 0 b t f 19 25 16 0 1227 0 0 nameicregexeq icregexeqsel icregexeqjoinsel ));
|
||||
#define OID_NAME_ICREGEXEQ_OP 1226
|
||||
DATA(insert OID = 1227 ( "!~*" PGUID 0 b t f 19 25 16 0 1226 0 0 nameicregexne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1228 ( "~*" PGUID 0 b t f 25 25 16 0 1229 0 0 texticregexeq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1227 ( "!~*" PGUID 0 b t f 19 25 16 0 1226 0 0 nameicregexne icregexnesel icregexnejoinsel ));
|
||||
DATA(insert OID = 1228 ( "~*" PGUID 0 b t f 25 25 16 0 1229 0 0 texticregexeq icregexeqsel icregexeqjoinsel ));
|
||||
#define OID_TEXT_ICREGEXEQ_OP 1228
|
||||
DATA(insert OID = 1229 ( "!~*" PGUID 0 b t f 25 25 16 0 1228 0 0 texticregexne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1232 ( "~*" PGUID 0 b t f 1043 25 16 0 1233 0 0 texticregexeq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1229 ( "!~*" PGUID 0 b t f 25 25 16 0 1228 0 0 texticregexne icregexnesel icregexnejoinsel ));
|
||||
DATA(insert OID = 1232 ( "~*" PGUID 0 b t f 1043 25 16 0 1233 0 0 texticregexeq icregexeqsel icregexeqjoinsel ));
|
||||
#define OID_VARCHAR_ICREGEXEQ_OP 1232
|
||||
DATA(insert OID = 1233 ( "!~*" PGUID 0 b t f 1043 25 16 0 1232 0 0 texticregexne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1234 ( "~*" PGUID 0 b t f 1042 25 16 0 1235 0 0 texticregexeq eqsel eqjoinsel ));
|
||||
DATA(insert OID = 1233 ( "!~*" PGUID 0 b t f 1043 25 16 0 1232 0 0 texticregexne icregexnesel icregexnejoinsel ));
|
||||
DATA(insert OID = 1234 ( "~*" PGUID 0 b t f 1042 25 16 0 1235 0 0 texticregexeq icregexeqsel icregexeqjoinsel ));
|
||||
#define OID_BPCHAR_ICREGEXEQ_OP 1234
|
||||
DATA(insert OID = 1235 ( "!~*" PGUID 0 b t f 1042 25 16 0 1234 0 0 texticregexne neqsel neqjoinsel ));
|
||||
DATA(insert OID = 1235 ( "!~*" PGUID 0 b t f 1042 25 16 0 1234 0 0 texticregexne icregexnesel icregexnejoinsel ));
|
||||
|
||||
/* timestamp operators */
|
||||
/* name, owner, prec, kind, isleft, canhash, left, right, result, com, negate, lsortop, rsortop, oprcode, operrest, oprjoin */
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pg_proc.h,v 1.147 2000/07/14 22:17:56 tgl Exp $
|
||||
* $Id: pg_proc.h,v 1.148 2000/07/17 03:05:25 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The script catalog/genbki.sh reads this file and generates .bki
|
||||
@ -433,8 +433,8 @@ DATA(insert OID = 206 ( float4um PGUID 11 f t t t 1 f 700 "700" 100 0 0 100
|
||||
DESCR("negate");
|
||||
DATA(insert OID = 207 ( float4abs PGUID 11 f t t t 1 f 700 "700" 100 0 0 100 float4abs - ));
|
||||
DESCR("absolute value");
|
||||
DATA(insert OID = 208 ( float4inc PGUID 11 f t t t 1 f 700 "700" 100 0 0 100 float4inc - ));
|
||||
DESCR("increment");
|
||||
DATA(insert OID = 208 ( float4_accum PGUID 12 f t t t 2 f 1022 "1022 700" 100 0 0 100 float4_accum - ));
|
||||
DESCR("aggregate transition function");
|
||||
DATA(insert OID = 209 ( float4larger PGUID 11 f t t t 2 f 700 "700 700" 100 0 0 100 float4larger - ));
|
||||
DESCR("larger of two");
|
||||
DATA(insert OID = 211 ( float4smaller PGUID 11 f t t t 2 f 700 "700 700" 100 0 0 100 float4smaller - ));
|
||||
@ -461,8 +461,8 @@ DATA(insert OID = 220 ( float8um PGUID 11 f t t t 1 f 701 "701" 100 0 0 100
|
||||
DESCR("negate");
|
||||
DATA(insert OID = 221 ( float8abs PGUID 11 f t t t 1 f 701 "701" 100 0 0 100 float8abs - ));
|
||||
DESCR("absolute value");
|
||||
DATA(insert OID = 222 ( float8inc PGUID 11 f t t t 1 f 701 "701" 100 0 0 100 float8inc - ));
|
||||
DESCR("increment");
|
||||
DATA(insert OID = 222 ( float8_accum PGUID 12 f t t t 2 f 1022 "1022 701" 100 0 0 100 float8_accum - ));
|
||||
DESCR("aggregate transition function");
|
||||
DATA(insert OID = 223 ( float8larger PGUID 11 f t t t 2 f 701 "701 701" 100 0 0 100 float8larger - ));
|
||||
DESCR("larger of two");
|
||||
DATA(insert OID = 224 ( float8smaller PGUID 11 f t t t 2 f 701 "701 701" 100 0 0 100 float8smaller - ));
|
||||
@ -1004,8 +1004,6 @@ DESCR("large object export");
|
||||
|
||||
DATA(insert OID = 766 ( int4inc PGUID 12 f t t t 1 f 23 "23" 100 0 0 100 int4inc - ));
|
||||
DESCR("increment");
|
||||
DATA(insert OID = 767 ( int2inc PGUID 12 f t t t 1 f 21 "21" 100 0 0 100 int2inc - ));
|
||||
DESCR("increment");
|
||||
DATA(insert OID = 768 ( int4larger PGUID 12 f t t t 2 f 23 "23 23" 100 0 0 100 int4larger - ));
|
||||
DESCR("larger of two");
|
||||
DATA(insert OID = 769 ( int4smaller PGUID 12 f t t t 2 f 23 "23 23" 100 0 0 100 int4smaller - ));
|
||||
@ -1181,8 +1179,6 @@ DATA(insert OID = 944 ( char PGUID 12 f t t t 1 f 18 "25" 100 0 0 100 tex
|
||||
DESCR("convert text to char");
|
||||
DATA(insert OID = 946 ( text PGUID 12 f t t t 1 f 25 "18" 100 0 0 100 char_text - ));
|
||||
DESCR("convert char to text");
|
||||
DATA(insert OID = 948 ( varchar PGUID 12 f t t t 1 f 25 "1043" 100 0 0 100 bpchar_char - ));
|
||||
DESCR("convert varchar() to text");
|
||||
|
||||
DATA(insert OID = 950 ( istrue PGUID 12 f t t f 1 f 16 "16" 100 0 0 100 istrue - ));
|
||||
DESCR("bool is true (not false or unknown)");
|
||||
@ -2395,8 +2391,6 @@ DATA(insert OID = 1746 ( float8 PGUID 11 f t t t 1 f 701 "1700" 100 0 0 100
|
||||
DESCR("(internal)");
|
||||
DATA(insert OID = 1764 ( numeric_inc PGUID 11 f t t t 1 f 1700 "1700" 100 0 0 100 numeric_inc - ));
|
||||
DESCR("increment by one");
|
||||
DATA(insert OID = 1765 ( numeric_dec PGUID 11 f t t t 1 f 1700 "1700" 100 0 0 100 numeric_dec - ));
|
||||
DESCR("decrement by one");
|
||||
DATA(insert OID = 1766 ( numeric_smaller PGUID 11 f t t t 2 f 1700 "1700 1700" 100 0 0 100 numeric_smaller - ));
|
||||
DESCR("smaller of two numbers");
|
||||
DATA(insert OID = 1767 ( numeric_larger PGUID 11 f t t t 2 f 1700 "1700 1700" 100 0 0 100 numeric_larger - ));
|
||||
@ -2407,7 +2401,7 @@ DATA(insert OID = 1771 ( numeric_uminus PGUID 11 f t t t 1 f 1700 "1700" 100 0
|
||||
DESCR("negate");
|
||||
DATA(insert OID = 1779 ( int8 PGUID 11 f t t t 1 f 20 "1700" 100 0 0 100 numeric_int8 - ));
|
||||
DESCR("(internal)");
|
||||
DATA(insert OID = 1781 ( numeric PGUID 11 f t t t 1 f 1700 "20" 100 0 0 100 int8_numeric - ));
|
||||
DATA(insert OID = 1781 ( numeric PGUID 12 f t t t 1 f 1700 "20" 100 0 0 100 int8_numeric - ));
|
||||
DESCR("(internal)");
|
||||
DATA(insert OID = 1782 ( numeric PGUID 12 f t t t 1 f 1700 "21" 100 0 0 100 int2_numeric - ));
|
||||
DESCR("(internal)");
|
||||
@ -2465,6 +2459,38 @@ DESCR("join selectivity of NOT LIKE");
|
||||
DATA(insert OID = 1829 ( icregexnejoinsel PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100 icregexnejoinsel - ));
|
||||
DESCR("join selectivity of case-insensitive regex non-match");
|
||||
|
||||
/* Aggregate-related functions */
|
||||
DATA(insert OID = 1830 ( float8_avg PGUID 12 f t t t 1 f 701 "1022" 100 0 0 100 float8_avg - ));
|
||||
DESCR("AVG aggregate final function");
|
||||
DATA(insert OID = 1831 ( float8_variance PGUID 12 f t t t 1 f 701 "1022" 100 0 0 100 float8_variance - ));
|
||||
DESCR("VARIANCE aggregate final function");
|
||||
DATA(insert OID = 1832 ( float8_stddev PGUID 12 f t t t 1 f 701 "1022" 100 0 0 100 float8_stddev - ));
|
||||
DESCR("STDDEV aggregate final function");
|
||||
DATA(insert OID = 1833 ( numeric_accum PGUID 12 f t t t 2 f 1231 "1231 1700" 100 0 0 100 numeric_accum - ));
|
||||
DESCR("aggregate transition function");
|
||||
DATA(insert OID = 1834 ( int2_accum PGUID 12 f t t t 2 f 1231 "1231 21" 100 0 0 100 int2_accum - ));
|
||||
DESCR("aggregate transition function");
|
||||
DATA(insert OID = 1835 ( int4_accum PGUID 12 f t t t 2 f 1231 "1231 23" 100 0 0 100 int4_accum - ));
|
||||
DESCR("aggregate transition function");
|
||||
DATA(insert OID = 1836 ( int8_accum PGUID 12 f t t t 2 f 1231 "1231 20" 100 0 0 100 int8_accum - ));
|
||||
DESCR("aggregate transition function");
|
||||
DATA(insert OID = 1837 ( numeric_avg PGUID 12 f t t t 1 f 1700 "1231" 100 0 0 100 numeric_avg - ));
|
||||
DESCR("AVG aggregate final function");
|
||||
DATA(insert OID = 1838 ( numeric_variance PGUID 12 f t t t 1 f 1700 "1231" 100 0 0 100 numeric_variance - ));
|
||||
DESCR("VARIANCE aggregate final function");
|
||||
DATA(insert OID = 1839 ( numeric_stddev PGUID 12 f t t t 1 f 1700 "1231" 100 0 0 100 numeric_stddev - ));
|
||||
DESCR("STDDEV aggregate final function");
|
||||
DATA(insert OID = 1840 ( int2_sum PGUID 12 f t t f 2 f 1700 "1700 21" 100 0 0 100 int2_sum - ));
|
||||
DESCR("SUM(int2) transition function");
|
||||
DATA(insert OID = 1841 ( int4_sum PGUID 12 f t t f 2 f 1700 "1700 23" 100 0 0 100 int4_sum - ));
|
||||
DESCR("SUM(int4) transition function");
|
||||
DATA(insert OID = 1842 ( int8_sum PGUID 12 f t t f 2 f 1700 "1700 20" 100 0 0 100 int8_sum - ));
|
||||
DESCR("SUM(int8) transition function");
|
||||
DATA(insert OID = 1843 ( interval_accum PGUID 12 f t t t 2 f 1187 "1187 1186" 100 0 0 100 interval_accum - ));
|
||||
DESCR("aggregate transition function");
|
||||
DATA(insert OID = 1844 ( interval_avg PGUID 12 f t t t 1 f 1186 "1187" 100 0 0 100 interval_avg - ));
|
||||
DESCR("AVG aggregate final function");
|
||||
|
||||
|
||||
/*
|
||||
* prototypes for functions pg_proc.c
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: primnodes.h,v 1.43 2000/06/12 19:40:49 momjian Exp $
|
||||
* $Id: primnodes.h,v 1.44 2000/07/17 03:05:27 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -301,10 +301,9 @@ typedef struct Iter
|
||||
* basetype - base type Oid of the aggregate (ie, input type)
|
||||
* aggtype - type Oid of final result of the aggregate
|
||||
* target - attribute or expression we are aggregating on
|
||||
* usenulls - TRUE to accept null values as inputs
|
||||
* aggstar - TRUE if argument was really '*'
|
||||
* aggdistinct - TRUE if arguments were labeled DISTINCT
|
||||
* aggno - workspace for nodeAgg.c executor
|
||||
* aggdistinct - TRUE if it's agg(DISTINCT ...)
|
||||
* aggno - workspace for executor (see nodeAgg.c)
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct Aggref
|
||||
@ -314,7 +313,6 @@ typedef struct Aggref
|
||||
Oid basetype;
|
||||
Oid aggtype;
|
||||
Node *target;
|
||||
bool usenulls;
|
||||
bool aggstar;
|
||||
bool aggdistinct;
|
||||
int aggno;
|
||||
|
@ -7,18 +7,16 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: lock.h,v 1.38 2000/05/31 00:28:38 petere Exp $
|
||||
* $Id: lock.h,v 1.39 2000/07/17 03:05:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef LOCK_H_
|
||||
#define LOCK_H_
|
||||
|
||||
#include "postgres.h"
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/itemptr.h"
|
||||
#include "storage/shmem.h"
|
||||
#include "utils/array.h"
|
||||
|
||||
extern SPINLOCK LockMgrLock;
|
||||
typedef int LOCKMASK;
|
||||
|
@ -11,7 +11,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: array.h,v 1.25 2000/06/13 07:35:30 tgl Exp $
|
||||
* $Id: array.h,v 1.26 2000/07/17 03:05:32 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* XXX the data array should be MAXALIGN'd -- notice that the array
|
||||
@ -24,15 +24,27 @@
|
||||
#define ARRAY_H
|
||||
|
||||
#include "fmgr.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
/*
|
||||
* Arrays are varlena objects, so must meet the varlena convention that
|
||||
* the first int32 of the object contains the total object size in bytes.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int size; /* total array size (in bytes) */
|
||||
int32 size; /* total array size (varlena requirement) */
|
||||
int ndim; /* # of dimensions */
|
||||
int flags; /* implementation flags */
|
||||
} ArrayType;
|
||||
|
||||
/*
|
||||
* fmgr macros for array objects
|
||||
*/
|
||||
#define DatumGetArrayTypeP(X) ((ArrayType *) PG_DETOAST_DATUM(X))
|
||||
#define DatumGetArrayTypePCopy(X) ((ArrayType *) PG_DETOAST_DATUM_COPY(X))
|
||||
#define PG_GETARG_ARRAYTYPE_P(n) DatumGetArrayTypeP(PG_GETARG_DATUM(n))
|
||||
#define PG_GETARG_ARRAYTYPE_P_COPY(n) DatumGetArrayTypePCopy(PG_GETARG_DATUM(n))
|
||||
#define PG_RETURN_ARRAYTYPE_P(x) PG_RETURN_POINTER(x)
|
||||
|
||||
/*
|
||||
* bitmask of ArrayType flags field:
|
||||
* 1st bit - large object flag
|
||||
@ -43,11 +55,9 @@ typedef struct
|
||||
#define ARR_CHK_FLAG (0x2)
|
||||
#define ARR_OBJ_MASK (0x1c)
|
||||
|
||||
#define ARR_FLAGS(a) ((ArrayType *) a)->flags
|
||||
#define ARR_SIZE(a) (((ArrayType *) a)->size)
|
||||
|
||||
#define ARR_NDIM(a) (((ArrayType *) a)->ndim)
|
||||
#define ARR_NDIM_PTR(a) (&(((ArrayType *) a)->ndim))
|
||||
#define ARR_FLAGS(a) (((ArrayType *) a)->flags)
|
||||
|
||||
#define ARR_IS_LO(a) \
|
||||
(((ArrayType *) a)->flags & ARR_LOB_FLAG)
|
||||
@ -102,7 +112,6 @@ typedef struct
|
||||
#define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0)
|
||||
|
||||
#define NAME_LEN 30
|
||||
#define MAX_BUFF_SIZE BLCKSZ
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -134,6 +143,12 @@ extern ArrayType *array_assgn(ArrayType *array, int nSubscripts,
|
||||
bool elmbyval, int elmlen, bool *isNull);
|
||||
extern Datum array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType);
|
||||
|
||||
extern ArrayType *construct_array(Datum *elems, int nelems,
|
||||
bool elmbyval, int elmlen, char elmalign);
|
||||
extern void deconstruct_array(ArrayType *array,
|
||||
bool elmbyval, int elmlen, char elmalign,
|
||||
Datum **elemsp, int *nelemsp);
|
||||
|
||||
extern int _LOtransfer(char **destfd, int size, int nitems, char **srcfd,
|
||||
int isSrcLO, int isDestLO);
|
||||
extern char *_array_newLO(int *fd, int flag);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: builtins.h,v 1.123 2000/07/09 21:30:21 petere Exp $
|
||||
* $Id: builtins.h,v 1.124 2000/07/17 03:05:32 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -110,7 +110,6 @@ extern Datum int2mi(PG_FUNCTION_ARGS);
|
||||
extern Datum int2mul(PG_FUNCTION_ARGS);
|
||||
extern Datum int2div(PG_FUNCTION_ARGS);
|
||||
extern Datum int2abs(PG_FUNCTION_ARGS);
|
||||
extern Datum int2inc(PG_FUNCTION_ARGS);
|
||||
extern Datum int24pl(PG_FUNCTION_ARGS);
|
||||
extern Datum int24mi(PG_FUNCTION_ARGS);
|
||||
extern Datum int24mul(PG_FUNCTION_ARGS);
|
||||
@ -207,12 +206,10 @@ extern float32 float4pl(float32 arg1, float32 arg2);
|
||||
extern float32 float4mi(float32 arg1, float32 arg2);
|
||||
extern float32 float4mul(float32 arg1, float32 arg2);
|
||||
extern float32 float4div(float32 arg1, float32 arg2);
|
||||
extern float32 float4inc(float32 arg1);
|
||||
extern float64 float8pl(float64 arg1, float64 arg2);
|
||||
extern float64 float8mi(float64 arg1, float64 arg2);
|
||||
extern float64 float8mul(float64 arg1, float64 arg2);
|
||||
extern float64 float8div(float64 arg1, float64 arg2);
|
||||
extern float64 float8inc(float64 arg1);
|
||||
extern bool float4eq(float32 arg1, float32 arg2);
|
||||
extern bool float4ne(float32 arg1, float32 arg2);
|
||||
extern bool float4lt(float32 arg1, float32 arg2);
|
||||
@ -261,6 +258,11 @@ extern float64 radians(float64 arg1);
|
||||
extern float64 dtan(float64 arg1);
|
||||
extern float64 drandom(void);
|
||||
extern int32 setseed(float64 seed);
|
||||
extern Datum float8_accum(PG_FUNCTION_ARGS);
|
||||
extern Datum float4_accum(PG_FUNCTION_ARGS);
|
||||
extern Datum float8_avg(PG_FUNCTION_ARGS);
|
||||
extern Datum float8_variance(PG_FUNCTION_ARGS);
|
||||
extern Datum float8_stddev(PG_FUNCTION_ARGS);
|
||||
|
||||
extern float64 float48pl(float32 arg1, float64 arg2);
|
||||
extern float64 float48mi(float32 arg1, float64 arg2);
|
||||
@ -545,7 +547,6 @@ extern Numeric numeric_mul(Numeric num1, Numeric num2);
|
||||
extern Numeric numeric_div(Numeric num1, Numeric num2);
|
||||
extern Numeric numeric_mod(Numeric num1, Numeric num2);
|
||||
extern Numeric numeric_inc(Numeric num);
|
||||
extern Numeric numeric_dec(Numeric num);
|
||||
extern Numeric numeric_smaller(Numeric num1, Numeric num2);
|
||||
extern Numeric numeric_larger(Numeric num1, Numeric num2);
|
||||
extern Numeric numeric_sqrt(Numeric num);
|
||||
@ -555,14 +556,24 @@ extern Numeric numeric_log(Numeric num1, Numeric num2);
|
||||
extern Numeric numeric_power(Numeric num1, Numeric num2);
|
||||
extern Datum int4_numeric(PG_FUNCTION_ARGS);
|
||||
extern int32 numeric_int4(Numeric num);
|
||||
extern Numeric int8_numeric(int64 *val);
|
||||
extern Datum int8_numeric(PG_FUNCTION_ARGS);
|
||||
extern int64 *numeric_int8(Numeric num);
|
||||
extern Datum int2_numeric(PG_FUNCTION_ARGS);
|
||||
extern Datum numeric_int2(PG_FUNCTION_ARGS);
|
||||
extern Numeric float4_numeric(float32 val);
|
||||
extern float32 numeric_float4(Numeric num);
|
||||
extern Numeric float8_numeric(float64 val);
|
||||
extern float64 numeric_float8(Numeric num);
|
||||
extern Numeric float4_numeric(float32 val);
|
||||
extern float32 numeric_float4(Numeric num);
|
||||
extern Datum numeric_accum(PG_FUNCTION_ARGS);
|
||||
extern Datum int2_accum(PG_FUNCTION_ARGS);
|
||||
extern Datum int4_accum(PG_FUNCTION_ARGS);
|
||||
extern Datum int8_accum(PG_FUNCTION_ARGS);
|
||||
extern Datum numeric_avg(PG_FUNCTION_ARGS);
|
||||
extern Datum numeric_variance(PG_FUNCTION_ARGS);
|
||||
extern Datum numeric_stddev(PG_FUNCTION_ARGS);
|
||||
extern Datum int2_sum(PG_FUNCTION_ARGS);
|
||||
extern Datum int4_sum(PG_FUNCTION_ARGS);
|
||||
extern Datum int8_sum(PG_FUNCTION_ARGS);
|
||||
|
||||
/* lztext.c */
|
||||
extern lztext *lztextin(char *str);
|
||||
|
@ -5,7 +5,7 @@
|
||||
*
|
||||
* 1998 Jan Wieck
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/include/utils/numeric.h,v 1.10 2000/06/13 07:35:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/include/utils/numeric.h,v 1.11 2000/07/17 03:05:32 tgl Exp $
|
||||
*
|
||||
* ----------
|
||||
*/
|
||||
@ -55,7 +55,7 @@
|
||||
* all leading and trailing zeroes (except there will be a trailing zero
|
||||
* in the last byte, if the number of digits is odd). In particular,
|
||||
* if the value is zero, there will be no digits at all! The weight is
|
||||
* arbitrary in this case, but we normally set it to zero.
|
||||
* arbitrary in that case, but we normally set it to zero.
|
||||
* ----------
|
||||
*/
|
||||
typedef struct NumericData
|
||||
@ -75,9 +75,11 @@ typedef NumericData *Numeric;
|
||||
* fmgr interface macros
|
||||
*/
|
||||
|
||||
#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
|
||||
#define NumericGetDatum(X) PointerGetDatum(X)
|
||||
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
|
||||
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
|
||||
#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
|
||||
#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X))
|
||||
#define NumericGetDatum(X) PointerGetDatum(X)
|
||||
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
|
||||
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
|
||||
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
|
||||
|
||||
#endif /* _PG_NUMERIC_H_ */
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: timestamp.h,v 1.8 2000/06/19 03:54:48 tgl Exp $
|
||||
* $Id: timestamp.h,v 1.9 2000/07/17 03:05:32 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -171,6 +171,8 @@ extern Datum interval_mi(PG_FUNCTION_ARGS);
|
||||
extern Datum interval_mul(PG_FUNCTION_ARGS);
|
||||
extern Datum mul_d_interval(PG_FUNCTION_ARGS);
|
||||
extern Datum interval_div(PG_FUNCTION_ARGS);
|
||||
extern Datum interval_accum(PG_FUNCTION_ARGS);
|
||||
extern Datum interval_avg(PG_FUNCTION_ARGS);
|
||||
|
||||
extern Datum timestamp_mi(PG_FUNCTION_ARGS);
|
||||
extern Datum timestamp_pl_span(PG_FUNCTION_ARGS);
|
||||
|
@ -386,28 +386,33 @@ create function tcl_int4add(int4,int4) returns int4 as '
|
||||
return [expr $1 + $2]
|
||||
' language 'pltcl';
|
||||
|
||||
create function tcl_int4div(int4,int4) returns int4 as '
|
||||
return [expr $1 / $2]
|
||||
-- We use split(n) as a quick-and-dirty way of parsing the input array
|
||||
-- value, which comes in as a string like '{1,2}'. There are better ways...
|
||||
|
||||
create function tcl_int4_accum(_int4,int4) returns _int4 as '
|
||||
set state [split $1 "{,}"]
|
||||
set newsum [expr {[lindex $state 1] + $2}]
|
||||
set newcnt [expr {[lindex $state 2] + 1}]
|
||||
return "{$newsum,$newcnt}"
|
||||
' language 'pltcl';
|
||||
|
||||
create function tcl_int4inc(int4) returns int4 as '
|
||||
return [expr $1 + 1]
|
||||
create function tcl_int4_avg(_int4) returns int4 as '
|
||||
set state [split $1 "{,}"]
|
||||
return [expr {[lindex $state 1] / [lindex $state 2]}]
|
||||
' language 'pltcl';
|
||||
|
||||
create aggregate tcl_avg (
|
||||
sfunc1 = tcl_int4add,
|
||||
sfunc = tcl_int4_accum,
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
sfunc2 = tcl_int4inc,
|
||||
stype2 = int4,
|
||||
finalfunc = tcl_int4div,
|
||||
initcond2 = '0'
|
||||
stype = _int4,
|
||||
finalfunc = tcl_int4_avg,
|
||||
initcond = '{0,0}'
|
||||
);
|
||||
|
||||
create aggregate tcl_sum (
|
||||
sfunc1 = tcl_int4add,
|
||||
sfunc = tcl_int4add,
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
stype = int4,
|
||||
initcond1 = '0'
|
||||
);
|
||||
|
||||
|
@ -2,18 +2,20 @@
|
||||
-- AGGREGATES
|
||||
--
|
||||
SELECT avg(four) AS avg_1 FROM onek;
|
||||
avg_1
|
||||
-------
|
||||
1
|
||||
avg_1
|
||||
--------------
|
||||
1.5000000000
|
||||
(1 row)
|
||||
|
||||
SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100;
|
||||
avg_32
|
||||
--------
|
||||
32
|
||||
avg_32
|
||||
---------------
|
||||
32.6666666667
|
||||
(1 row)
|
||||
|
||||
SELECT avg(b) AS avg_107_943 FROM aggtest;
|
||||
-- In 7.1, avg(float4) is computed using float8 arithmetic.
|
||||
-- Round the result to 3 digits to avoid platform-specific results.
|
||||
SELECT avg(b)::numeric(10,3) AS avg_107_943 FROM aggtest;
|
||||
avg_107_943
|
||||
-------------
|
||||
107.943
|
||||
@ -116,9 +118,9 @@ select ten, count(four), sum(DISTINCT four) from onek group by ten;
|
||||
(10 rows)
|
||||
|
||||
SELECT newavg(four) AS avg_1 FROM onek;
|
||||
avg_1
|
||||
-------
|
||||
1
|
||||
avg_1
|
||||
--------------
|
||||
1.5000000000
|
||||
(1 row)
|
||||
|
||||
SELECT newsum(four) AS sum_1500 FROM onek;
|
||||
|
@ -3,18 +3,17 @@
|
||||
--
|
||||
-- all functions CREATEd
|
||||
CREATE AGGREGATE newavg (
|
||||
sfunc1 = int4pl, basetype = int4, stype1 = int4,
|
||||
sfunc2 = int4inc, stype2 = int4,
|
||||
finalfunc = int4div,
|
||||
initcond1 = '0', initcond2 = '0'
|
||||
sfunc = int4_accum, basetype = int4, stype = _numeric,
|
||||
finalfunc = numeric_avg,
|
||||
initcond1 = '{0,0,0}'
|
||||
);
|
||||
-- sfunc1 (value-dependent) only
|
||||
-- without finalfunc; test obsolete spellings 'sfunc1' etc
|
||||
CREATE AGGREGATE newsum (
|
||||
sfunc1 = int4pl, basetype = int4, stype1 = int4,
|
||||
initcond1 = '0'
|
||||
);
|
||||
-- sfunc2 (value-independent) only
|
||||
-- value-independent transition function
|
||||
CREATE AGGREGATE newcnt (
|
||||
sfunc2 = int4inc, basetype = int4, stype2 = int4,
|
||||
initcond2 = '0'
|
||||
sfunc = int4inc, basetype = 'any', stype = int4,
|
||||
initcond = '0'
|
||||
);
|
||||
|
@ -99,46 +99,18 @@ end;
|
||||
NOTICE: COMMIT: no transaction in progress
|
||||
--
|
||||
-- DEFINE AGGREGATE
|
||||
|
||||
-- left out finalfunc
|
||||
create aggregate newavg1 (sfunc1 = int4pl,
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
sfunc2 = int4inc,
|
||||
stype2 = int4,
|
||||
initcond1 = '0',
|
||||
initcond2 = '0');
|
||||
ERROR: AggregateCreate: Aggregate must have final function with both transition functions
|
||||
-- sfunc return type disagreement
|
||||
create aggregate newavg2 (sfunc1 = int4pl,
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
sfunc2 = int2inc,
|
||||
stype2 = int2,
|
||||
finalfunc = int4div,
|
||||
initcond1 = '0',
|
||||
initcond2 = '0');
|
||||
ERROR: AggregateCreate: 'int4div'('int4','int2') does not exist
|
||||
-- sfunc/finalfunc type disagreement
|
||||
create aggregate newavg3 (sfunc1 = int4pl,
|
||||
create aggregate newavg2 (sfunc = int4pl,
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
sfunc2 = int4inc,
|
||||
stype2 = int4,
|
||||
finalfunc = int2div,
|
||||
initcond1 = '0',
|
||||
initcond2 = '0');
|
||||
ERROR: AggregateCreate: 'int2div'('int4','int4') does not exist
|
||||
stype = int4,
|
||||
finalfunc = int2um,
|
||||
initcond = '0');
|
||||
ERROR: AggregateCreate: function 'int2um(int4)' does not exist
|
||||
-- left out basetype
|
||||
create aggregate newcnt1 (sfunc2 = int4inc,
|
||||
stype2 = int4,
|
||||
initcond2 = '0');
|
||||
create aggregate newcnt1 (sfunc = int4inc,
|
||||
stype = int4,
|
||||
initcond = '0');
|
||||
ERROR: Define: "basetype" unspecified
|
||||
-- left out initcond2 (for sfunc2)
|
||||
create aggregate newcnt1 (sfunc2 = int4inc,
|
||||
basetype = int4,
|
||||
stype2 = int4);
|
||||
ERROR: AggregateCreate: transition function 2 MUST have an initial value
|
||||
--
|
||||
-- REMOVE INDEX
|
||||
|
||||
|
@ -1,20 +1,12 @@
|
||||
--
|
||||
-- This is created by pgsql/contrib/findoidjoins/make_oidjoin_check
|
||||
--
|
||||
SELECT oid, pg_aggregate.aggtransfn1
|
||||
SELECT oid, pg_aggregate.aggtransfn
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggtransfn1 != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn1);
|
||||
oid | aggtransfn1
|
||||
-----+-------------
|
||||
(0 rows)
|
||||
|
||||
SELECT oid, pg_aggregate.aggtransfn2
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggtransfn2 != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn2);
|
||||
oid | aggtransfn2
|
||||
-----+-------------
|
||||
WHERE pg_aggregate.aggtransfn != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn);
|
||||
oid | aggtransfn
|
||||
-----+------------
|
||||
(0 rows)
|
||||
|
||||
SELECT oid, pg_aggregate.aggfinalfn
|
||||
@ -33,20 +25,12 @@ WHERE pg_aggregate.aggbasetype != 0 AND
|
||||
-----+-------------
|
||||
(0 rows)
|
||||
|
||||
SELECT oid, pg_aggregate.aggtranstype1
|
||||
SELECT oid, pg_aggregate.aggtranstype
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggtranstype1 != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype1);
|
||||
oid | aggtranstype1
|
||||
-----+---------------
|
||||
(0 rows)
|
||||
|
||||
SELECT oid, pg_aggregate.aggtranstype2
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggtranstype2 != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype2);
|
||||
oid | aggtranstype2
|
||||
-----+---------------
|
||||
WHERE pg_aggregate.aggtranstype != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype);
|
||||
oid | aggtranstype
|
||||
-----+--------------
|
||||
(0 rows)
|
||||
|
||||
SELECT oid, pg_aggregate.aggfinaltype
|
||||
@ -217,6 +201,22 @@ WHERE pg_class.relam != 0 AND
|
||||
-----+-------
|
||||
(0 rows)
|
||||
|
||||
SELECT oid, pg_class.reltoastrelid
|
||||
FROM pg_class
|
||||
WHERE pg_class.reltoastrelid != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_class AS t1 WHERE t1.oid = pg_class.reltoastrelid);
|
||||
oid | reltoastrelid
|
||||
-----+---------------
|
||||
(0 rows)
|
||||
|
||||
SELECT oid, pg_class.reltoastidxid
|
||||
FROM pg_class
|
||||
WHERE pg_class.reltoastidxid != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_class AS t1 WHERE t1.oid = pg_class.reltoastidxid);
|
||||
oid | reltoastidxid
|
||||
-----+---------------
|
||||
(0 rows)
|
||||
|
||||
SELECT oid, pg_index.indexrelid
|
||||
FROM pg_index
|
||||
WHERE pg_index.indexrelid != 0 AND
|
||||
|
@ -84,9 +84,8 @@ WHERE p1.oid != p2.oid AND
|
||||
(p1.prorettype < p2.prorettype);
|
||||
prorettype | prorettype
|
||||
------------+------------
|
||||
18 | 25
|
||||
25 | 1043
|
||||
(2 rows)
|
||||
(1 row)
|
||||
|
||||
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
|
||||
FROM pg_proc AS p1, pg_proc AS p2
|
||||
@ -98,8 +97,7 @@ WHERE p1.oid != p2.oid AND
|
||||
proargtypes | proargtypes
|
||||
-------------+-------------
|
||||
25 | 1043
|
||||
1042 | 1043
|
||||
(2 rows)
|
||||
(1 row)
|
||||
|
||||
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
|
||||
FROM pg_proc AS p1, pg_proc AS p2
|
||||
@ -434,83 +432,40 @@ WHERE p1.oprjoin = p2.oid AND
|
||||
|
||||
-- **************** pg_aggregate ****************
|
||||
-- Look for illegal values in pg_aggregate fields.
|
||||
-- aggbasetype can only be 0 if transfn1 is not present (eg, count(*))
|
||||
-- or itself takes a wild-card input; we check the latter case below.
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE (p1.aggbasetype = 0 AND p1.aggtransfn1 != 0) OR aggfinaltype = 0;
|
||||
WHERE aggtransfn = 0 OR aggtranstype = 0 OR aggfinaltype = 0;
|
||||
oid | aggname
|
||||
-----+---------
|
||||
(0 rows)
|
||||
|
||||
-- Check combinations of transfer functions.
|
||||
-- Although either transfn1 or transfn2 can be null,
|
||||
-- it makes no sense for both to be. And if both are defined,
|
||||
-- presumably there should be a finalfn to combine their results.
|
||||
-- We also check that transtypes are null just when corresponding
|
||||
-- transfns are. Also, if there is no finalfn then the output type
|
||||
-- must be the transtype the result will be taken from.
|
||||
-- If there is no finalfn then the output type must be the transtype.
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 = 0;
|
||||
WHERE p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype;
|
||||
oid | aggname
|
||||
-----+---------
|
||||
(0 rows)
|
||||
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE p1.aggtransfn1 != 0 AND p1.aggtransfn2 = 0 AND
|
||||
(p1.aggtranstype1 = 0 OR p1.aggtranstype2 != 0 OR
|
||||
(p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype1));
|
||||
oid | aggname
|
||||
-----+---------
|
||||
(0 rows)
|
||||
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 != 0 AND
|
||||
(p1.aggtranstype1 != 0 OR p1.aggtranstype2 = 0 OR
|
||||
(p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype2));
|
||||
oid | aggname
|
||||
-----+---------
|
||||
(0 rows)
|
||||
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE p1.aggtransfn1 != 0 AND p1.aggtransfn2 != 0 AND
|
||||
(p1.aggtranstype1 = 0 OR p1.aggtranstype2 = 0 OR
|
||||
p1.aggfinalfn = 0);
|
||||
oid | aggname
|
||||
-----+---------
|
||||
(0 rows)
|
||||
|
||||
-- Cross-check transfn1 (if present) against its entry in pg_proc.
|
||||
-- Cross-check transfn against its entry in pg_proc.
|
||||
-- FIXME: what about binary-compatible types?
|
||||
-- NOTE: in 7.1, this search finds max and min on abstime, which are
|
||||
-- implemented using int4larger/int4smaller. Until we have
|
||||
-- some cleaner way of dealing with binary-equivalent types, just leave
|
||||
-- those two tuples in the expected output.
|
||||
SELECT p1.oid, p1.aggname, p2.oid, p2.proname
|
||||
FROM pg_aggregate AS p1, pg_proc AS p2
|
||||
WHERE p1.aggtransfn1 = p2.oid AND
|
||||
(p2.proretset OR p2.pronargs != 2
|
||||
-- diked out until we find a way of marking binary-compatible types
|
||||
-- OR
|
||||
-- p1.aggtranstype1 != p2.prorettype OR
|
||||
-- p1.aggtranstype1 != p2.proargtypes[0] OR
|
||||
-- p1.aggbasetype != p2.proargtypes[1]
|
||||
);
|
||||
oid | aggname | oid | proname
|
||||
-----+---------+-----+---------
|
||||
(0 rows)
|
||||
|
||||
-- Cross-check transfn2 (if present) against its entry in pg_proc.
|
||||
-- FIXME: what about binary-compatible types?
|
||||
SELECT p1.oid, p1.aggname, p2.oid, p2.proname
|
||||
FROM pg_aggregate AS p1, pg_proc AS p2
|
||||
WHERE p1.aggtransfn2 = p2.oid AND
|
||||
(p2.proretset OR p1.aggtranstype2 != p2.prorettype OR
|
||||
p2.pronargs != 1 OR
|
||||
p1.aggtranstype2 != p2.proargtypes[0]);
|
||||
oid | aggname | oid | proname
|
||||
-----+---------+-----+---------
|
||||
(0 rows)
|
||||
WHERE p1.aggtransfn = p2.oid AND
|
||||
(p2.proretset OR
|
||||
p1.aggtranstype != p2.prorettype OR
|
||||
p1.aggtranstype != p2.proargtypes[0] OR
|
||||
NOT ((p2.pronargs = 2 AND p1.aggbasetype = p2.proargtypes[1]) OR
|
||||
(p2.pronargs = 1 AND p1.aggbasetype = 0)));
|
||||
oid | aggname | oid | proname
|
||||
-------+---------+-----+-------------
|
||||
16978 | max | 768 | int4larger
|
||||
16992 | min | 769 | int4smaller
|
||||
(2 rows)
|
||||
|
||||
-- Cross-check finalfn (if present) against its entry in pg_proc.
|
||||
-- FIXME: what about binary-compatible types?
|
||||
@ -518,9 +473,8 @@ SELECT p1.oid, p1.aggname, p2.oid, p2.proname
|
||||
FROM pg_aggregate AS p1, pg_proc AS p2
|
||||
WHERE p1.aggfinalfn = p2.oid AND
|
||||
(p2.proretset OR p1.aggfinaltype != p2.prorettype OR
|
||||
p2.pronargs != 2 OR
|
||||
p1.aggtranstype1 != p2.proargtypes[0] OR
|
||||
p1.aggtranstype2 != p2.proargtypes[1]);
|
||||
p2.pronargs != 1 OR
|
||||
p1.aggtranstype != p2.proargtypes[0]);
|
||||
oid | aggname | oid | proname
|
||||
-----+---------+-----+---------
|
||||
(0 rows)
|
||||
|
@ -6,7 +6,10 @@ SELECT avg(four) AS avg_1 FROM onek;
|
||||
|
||||
SELECT avg(a) AS avg_32 FROM aggtest WHERE a < 100;
|
||||
|
||||
SELECT avg(b) AS avg_107_943 FROM aggtest;
|
||||
-- In 7.1, avg(float4) is computed using float8 arithmetic.
|
||||
-- Round the result to 3 digits to avoid platform-specific results.
|
||||
|
||||
SELECT avg(b)::numeric(10,3) AS avg_107_943 FROM aggtest;
|
||||
|
||||
SELECT avg(gpa) AS avg_3_4 FROM ONLY student;
|
||||
|
||||
|
@ -4,21 +4,20 @@
|
||||
|
||||
-- all functions CREATEd
|
||||
CREATE AGGREGATE newavg (
|
||||
sfunc1 = int4pl, basetype = int4, stype1 = int4,
|
||||
sfunc2 = int4inc, stype2 = int4,
|
||||
finalfunc = int4div,
|
||||
initcond1 = '0', initcond2 = '0'
|
||||
sfunc = int4_accum, basetype = int4, stype = _numeric,
|
||||
finalfunc = numeric_avg,
|
||||
initcond1 = '{0,0,0}'
|
||||
);
|
||||
|
||||
-- sfunc1 (value-dependent) only
|
||||
-- without finalfunc; test obsolete spellings 'sfunc1' etc
|
||||
CREATE AGGREGATE newsum (
|
||||
sfunc1 = int4pl, basetype = int4, stype1 = int4,
|
||||
initcond1 = '0'
|
||||
);
|
||||
|
||||
-- sfunc2 (value-independent) only
|
||||
-- value-independent transition function
|
||||
CREATE AGGREGATE newcnt (
|
||||
sfunc2 = int4inc, basetype = int4, stype2 = int4,
|
||||
initcond2 = '0'
|
||||
sfunc = int4inc, basetype = 'any', stype = int4,
|
||||
initcond = '0'
|
||||
);
|
||||
|
||||
|
@ -115,44 +115,17 @@ end;
|
||||
--
|
||||
-- DEFINE AGGREGATE
|
||||
|
||||
-- left out finalfunc
|
||||
create aggregate newavg1 (sfunc1 = int4pl,
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
sfunc2 = int4inc,
|
||||
stype2 = int4,
|
||||
initcond1 = '0',
|
||||
initcond2 = '0');
|
||||
|
||||
-- sfunc return type disagreement
|
||||
create aggregate newavg2 (sfunc1 = int4pl,
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
sfunc2 = int2inc,
|
||||
stype2 = int2,
|
||||
finalfunc = int4div,
|
||||
initcond1 = '0',
|
||||
initcond2 = '0');
|
||||
|
||||
-- sfunc/finalfunc type disagreement
|
||||
create aggregate newavg3 (sfunc1 = int4pl,
|
||||
create aggregate newavg2 (sfunc = int4pl,
|
||||
basetype = int4,
|
||||
stype1 = int4,
|
||||
sfunc2 = int4inc,
|
||||
stype2 = int4,
|
||||
finalfunc = int2div,
|
||||
initcond1 = '0',
|
||||
initcond2 = '0');
|
||||
stype = int4,
|
||||
finalfunc = int2um,
|
||||
initcond = '0');
|
||||
|
||||
-- left out basetype
|
||||
create aggregate newcnt1 (sfunc2 = int4inc,
|
||||
stype2 = int4,
|
||||
initcond2 = '0');
|
||||
|
||||
-- left out initcond2 (for sfunc2)
|
||||
create aggregate newcnt1 (sfunc2 = int4inc,
|
||||
basetype = int4,
|
||||
stype2 = int4);
|
||||
create aggregate newcnt1 (sfunc = int4inc,
|
||||
stype = int4,
|
||||
initcond = '0');
|
||||
|
||||
|
||||
--
|
||||
|
@ -1,14 +1,10 @@
|
||||
--
|
||||
-- This is created by pgsql/contrib/findoidjoins/make_oidjoin_check
|
||||
--
|
||||
SELECT oid, pg_aggregate.aggtransfn1
|
||||
SELECT oid, pg_aggregate.aggtransfn
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggtransfn1 != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn1);
|
||||
SELECT oid, pg_aggregate.aggtransfn2
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggtransfn2 != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn2);
|
||||
WHERE pg_aggregate.aggtransfn != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_proc AS t1 WHERE t1.oid = pg_aggregate.aggtransfn);
|
||||
SELECT oid, pg_aggregate.aggfinalfn
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggfinalfn != 0 AND
|
||||
@ -17,14 +13,10 @@ SELECT oid, pg_aggregate.aggbasetype
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggbasetype != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggbasetype);
|
||||
SELECT oid, pg_aggregate.aggtranstype1
|
||||
SELECT oid, pg_aggregate.aggtranstype
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggtranstype1 != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype1);
|
||||
SELECT oid, pg_aggregate.aggtranstype2
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggtranstype2 != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype2);
|
||||
WHERE pg_aggregate.aggtranstype != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_type AS t1 WHERE t1.oid = pg_aggregate.aggtranstype);
|
||||
SELECT oid, pg_aggregate.aggfinaltype
|
||||
FROM pg_aggregate
|
||||
WHERE pg_aggregate.aggfinaltype != 0 AND
|
||||
@ -109,6 +101,14 @@ SELECT oid, pg_class.relam
|
||||
FROM pg_class
|
||||
WHERE pg_class.relam != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_am AS t1 WHERE t1.oid = pg_class.relam);
|
||||
SELECT oid, pg_class.reltoastrelid
|
||||
FROM pg_class
|
||||
WHERE pg_class.reltoastrelid != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_class AS t1 WHERE t1.oid = pg_class.reltoastrelid);
|
||||
SELECT oid, pg_class.reltoastidxid
|
||||
FROM pg_class
|
||||
WHERE pg_class.reltoastidxid != 0 AND
|
||||
NOT EXISTS(SELECT * FROM pg_class AS t1 WHERE t1.oid = pg_class.reltoastidxid);
|
||||
SELECT oid, pg_index.indexrelid
|
||||
FROM pg_index
|
||||
WHERE pg_index.indexrelid != 0 AND
|
||||
|
@ -363,66 +363,32 @@ WHERE p1.oprjoin = p2.oid AND
|
||||
-- **************** pg_aggregate ****************
|
||||
|
||||
-- Look for illegal values in pg_aggregate fields.
|
||||
-- aggbasetype can only be 0 if transfn1 is not present (eg, count(*))
|
||||
-- or itself takes a wild-card input; we check the latter case below.
|
||||
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE (p1.aggbasetype = 0 AND p1.aggtransfn1 != 0) OR aggfinaltype = 0;
|
||||
WHERE aggtransfn = 0 OR aggtranstype = 0 OR aggfinaltype = 0;
|
||||
|
||||
-- Check combinations of transfer functions.
|
||||
-- Although either transfn1 or transfn2 can be null,
|
||||
-- it makes no sense for both to be. And if both are defined,
|
||||
-- presumably there should be a finalfn to combine their results.
|
||||
-- We also check that transtypes are null just when corresponding
|
||||
-- transfns are. Also, if there is no finalfn then the output type
|
||||
-- must be the transtype the result will be taken from.
|
||||
-- If there is no finalfn then the output type must be the transtype.
|
||||
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 = 0;
|
||||
WHERE p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype;
|
||||
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE p1.aggtransfn1 != 0 AND p1.aggtransfn2 = 0 AND
|
||||
(p1.aggtranstype1 = 0 OR p1.aggtranstype2 != 0 OR
|
||||
(p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype1));
|
||||
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 != 0 AND
|
||||
(p1.aggtranstype1 != 0 OR p1.aggtranstype2 = 0 OR
|
||||
(p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype2));
|
||||
|
||||
SELECT p1.oid, p1.aggname
|
||||
FROM pg_aggregate as p1
|
||||
WHERE p1.aggtransfn1 != 0 AND p1.aggtransfn2 != 0 AND
|
||||
(p1.aggtranstype1 = 0 OR p1.aggtranstype2 = 0 OR
|
||||
p1.aggfinalfn = 0);
|
||||
|
||||
-- Cross-check transfn1 (if present) against its entry in pg_proc.
|
||||
-- Cross-check transfn against its entry in pg_proc.
|
||||
-- FIXME: what about binary-compatible types?
|
||||
-- NOTE: in 7.1, this search finds max and min on abstime, which are
|
||||
-- implemented using int4larger/int4smaller. Until we have
|
||||
-- some cleaner way of dealing with binary-equivalent types, just leave
|
||||
-- those two tuples in the expected output.
|
||||
|
||||
SELECT p1.oid, p1.aggname, p2.oid, p2.proname
|
||||
FROM pg_aggregate AS p1, pg_proc AS p2
|
||||
WHERE p1.aggtransfn1 = p2.oid AND
|
||||
(p2.proretset OR p2.pronargs != 2
|
||||
-- diked out until we find a way of marking binary-compatible types
|
||||
-- OR
|
||||
-- p1.aggtranstype1 != p2.prorettype OR
|
||||
-- p1.aggtranstype1 != p2.proargtypes[0] OR
|
||||
-- p1.aggbasetype != p2.proargtypes[1]
|
||||
);
|
||||
|
||||
-- Cross-check transfn2 (if present) against its entry in pg_proc.
|
||||
-- FIXME: what about binary-compatible types?
|
||||
|
||||
SELECT p1.oid, p1.aggname, p2.oid, p2.proname
|
||||
FROM pg_aggregate AS p1, pg_proc AS p2
|
||||
WHERE p1.aggtransfn2 = p2.oid AND
|
||||
(p2.proretset OR p1.aggtranstype2 != p2.prorettype OR
|
||||
p2.pronargs != 1 OR
|
||||
p1.aggtranstype2 != p2.proargtypes[0]);
|
||||
WHERE p1.aggtransfn = p2.oid AND
|
||||
(p2.proretset OR
|
||||
p1.aggtranstype != p2.prorettype OR
|
||||
p1.aggtranstype != p2.proargtypes[0] OR
|
||||
NOT ((p2.pronargs = 2 AND p1.aggbasetype = p2.proargtypes[1]) OR
|
||||
(p2.pronargs = 1 AND p1.aggbasetype = 0)));
|
||||
|
||||
-- Cross-check finalfn (if present) against its entry in pg_proc.
|
||||
-- FIXME: what about binary-compatible types?
|
||||
@ -431,9 +397,8 @@ SELECT p1.oid, p1.aggname, p2.oid, p2.proname
|
||||
FROM pg_aggregate AS p1, pg_proc AS p2
|
||||
WHERE p1.aggfinalfn = p2.oid AND
|
||||
(p2.proretset OR p1.aggfinaltype != p2.prorettype OR
|
||||
p2.pronargs != 2 OR
|
||||
p1.aggtranstype1 != p2.proargtypes[0] OR
|
||||
p1.aggtranstype2 != p2.proargtypes[1]);
|
||||
p2.pronargs != 1 OR
|
||||
p1.aggtranstype != p2.proargtypes[0]);
|
||||
|
||||
-- **************** pg_opclass ****************
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
--
|
||||
-- Copyright (c) 1994, Regents of the University of California
|
||||
--
|
||||
-- $Id: complex.source,v 1.7 2000/03/28 02:49:19 tgl Exp $
|
||||
-- $Id: complex.source,v 1.8 2000/07/17 03:05:41 tgl Exp $
|
||||
--
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
@ -111,10 +111,10 @@ SELECT a + '(1.0,1.0)'::complex AS aa,
|
||||
-----------------------------
|
||||
|
||||
CREATE AGGREGATE complex_sum (
|
||||
sfunc1 = complex_add,
|
||||
sfunc = complex_add,
|
||||
basetype = complex,
|
||||
stype1 = complex,
|
||||
initcond1 = '(0,0)'
|
||||
stype = complex,
|
||||
initcond = '(0,0)'
|
||||
);
|
||||
|
||||
SELECT complex_sum(a) FROM test_complex;
|
||||
|
Loading…
Reference in New Issue
Block a user