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 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
* 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
<!--
-$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
-->
</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
<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.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><replaceable class="PARAMETER">state1_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.
+ 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">state2_type</replaceable></term>
+ <term><replaceable class="PARAMETER">state_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>
<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>
<refsect1 id="R1-SQL-CREATEAGGREGATE-1">
<refsect1info>
- <date>1998-09-09</date>
+ <date>2000-07-16</date>
</refsect1info>
<title>
Description
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.
+ value. If there is no final function then the ending state value
+ is returned as-is.
</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).
- </para>
+ 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>
- 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.
+ <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
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>
<!--
-$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
-->
<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>
</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>
<!--
-$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">
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
<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;
</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>
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
* 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.
*
*/
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));
if (!aggName)
elog(ERROR, "AggregateCreate: no aggregate name supplied");
- if (!aggtransfn1Name && !aggtransfn2Name)
- elog(ERROR, "AggregateCreate: aggregate must have at least one transition function");
-
- if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
- elog(ERROR, "AggregateCreate: Aggregate must have final function with both transition functions");
+ if (!aggtransfnName)
+ elog(ERROR, "AggregateCreate: aggregate must have a transition function");
- /* 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)
+ /* 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));
+
+ /* handle transfn */
+ fnArgs[0] = transtype;
+ if (OidIsValid(basetype))
{
- tup = SearchSysCacheTuple(TYPENAME,
- PointerGetDatum(aggtransfn1typeName),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "AggregateCreate: Type '%s' undefined",
- aggtransfn1typeName);
- xret1 = tup->t_data->t_oid;
-
- 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);
+ fnArgs[1] = basetype;
+ nargs = 2;
}
-
- /* handle transfn2 and transtype2 */
- if (aggtransfn2Name)
+ else
{
- 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[1] = 0;
- tup = SearchSysCacheTuple(PROCNAME,
- PointerGetDatum(aggtransfn2Name),
- 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);
+ 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 finalfn */
+ /* handle finalfn, if supplied */
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 */
+ fnArgs[0] = transtype;
+ fnArgs[1] = 0;
tup = SearchSysCacheTuple(PROCNAME,
PointerGetDatum(aggfinalfnName),
- Int32GetDatum(nargs),
+ Int32GetDatum(1),
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++)
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);
-
- if (agginitval1)
- values[Anum_pg_aggregate_agginitval1 - 1] =
- DirectFunctionCall1(textin, CStringGetDatum(agginitval1));
- else
- nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
-
- if (agginitval2)
- values[Anum_pg_aggregate_agginitval2 - 1] =
- DirectFunctionCall1(textin, CStringGetDatum(agginitval2));
+ 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 (agginitval)
+ values[Anum_pg_aggregate_agginitval - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(agginitval));
else
- nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
+ nulls[Anum_pg_aggregate_agginitval - 1] = 'n';
aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock);
tupDesc = aggdesc->rd_att;
}
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;
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),
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),
*
*
* 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
*/
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)
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);
- }
}
/*
*/
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 */
}
/*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
*
*
* 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
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
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;
/*
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
*/
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,
}
/*
- * (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.
*/
{
/*
* 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 =
/* 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.
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;
*resultIsNull = fcinfo.isnull;
}
}
- else if (OidIsValid(peraggstate->xfn1_oid))
- {
- /* Return value1 */
- *resultVal = peraggstate->value1;
- *resultIsNull = peraggstate->value1IsNull;
- }
- else if (OidIsValid(peraggstate->xfn2_oid))
+ else
{
- /* Return value2 */
- *resultVal = peraggstate->value2;
- *resultIsNull = peraggstate->value2IsNull;
+ *resultVal = peraggstate->transValue;
+ *resultIsNull = peraggstate->transValueIsNull;
}
- else
- elog(ERROR, "ExecAgg: no valid transition functions??");
+
/*
* If result is pass-by-ref, make sure it is in the right context.
*/
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,
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
}
else
- advance_transition_functions(peraggstate,
- newVal, isNull);
+ {
+ advance_transition_function(peraggstate,
+ newVal, isNull);
+ }
}
/*
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 */
peraggstate->resulttypeLen = typeLen(typeInfo);
peraggstate->resulttypeByVal = typeByVal(typeInfo);
- peraggstate->initValue1 =
- AggNameGetInitVal(aggname,
- aggform->aggbasetype,
- 1,
- &peraggstate->initValue1IsNull);
+ typeInfo = typeidType(aggform->aggtranstype);
+ peraggstate->transtypeLen = typeLen(typeInfo);
+ peraggstate->transtypeByVal = typeByVal(typeInfo);
- peraggstate->initValue2 =
+ peraggstate->initValue =
AggNameGetInitVal(aggname,
aggform->aggbasetype,
- 2,
- &peraggstate->initValue2IsNull);
+ &peraggstate->initValueIsNull);
- 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);
- }
+ fmgr_info(transfn_oid, &peraggstate->transfn);
+ if (OidIsValid(finalfn_oid))
+ fmgr_info(finalfn_oid, &peraggstate->finalfn);
- if (OidIsValid(xfn2_oid))
+ /*
+ * 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)
{
- 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.
- * ------------------------------------------
+ /*
+ * Note: use the type from the input expression here,
+ * not aggform->aggbasetype, because the latter might be 0.
+ * (Consider COUNT(*).)
*/
- if (peraggstate->initValue2IsNull)
- elog(ERROR, "ExecInitAgg: agginitval2 is null");
- }
+ Oid inputType = exprType(aggref->target);
- if (OidIsValid(finalfn_oid))
- fmgr_info(finalfn_oid, &peraggstate->finalfn);
+ 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;
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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)
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "executor/executor.h"
#include "executor/nodeResult.h"
+#include "utils/memutils.h"
+
/* ----------------------------------------------------------------
* ExecResult(node)
*
*
* 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
#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> */
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
* 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
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 */
*
*
* 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
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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.
* 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;
*/
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));
}
*
*
* 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
*
*
*-------------------------------------------------------------------------
*/
+
#include "postgres.h"
#include <unistd.h>
#include "tcop/tcopprot.h"
#include "utils/exc.h"
#include "utils/guc.h"
+#include "utils/memutils.h"
#define INVALID_SOCK (-1)
*
*
* 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
* Interface:
*
* LockAcquire(), LockRelease(), LockMethodTableInit(),
- * LockMethodTableRename(), LockReleaseAll, LockOwners()
+ * LockMethodTableRename(), LockReleaseAll,
* LockResolveConflicts(), GrantLock()
*
* NOTE: This module is used to define new lock tables. The
#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);
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
*
*
* 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
#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
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");
}
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
#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
* <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);
int i,
nitems;
int32 nbytes;
- char *dataPtr;
+ Datum *dataPtr;
ArrayType *retval;
int ndim,
dim[MAXDIM],
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));
-
- /*
- * 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);
+ memcpy((char *) ARR_DIMS(retval), (char *) dim,
+ ndim * sizeof(int));
+ memcpy((char *) ARR_LBOUND(retval), (char *) lBound,
+ ndim * sizeof(int));
+
+ CopyArrayEls(ARR_DATA_PTR(retval), dataPtr, nitems,
+ typbyval, typlen, typalign, true);
+ pfree(dataPtr);
}
else
{
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));
PG_RETURN_NULL();
}
pfree(string_save);
- PG_RETURN_POINTER(retval);
+ PG_RETURN_ARRAYTYPE_P(retval);
}
/*-----------------------------------------------------------------------------
}
/*---------------------------------------------------------------------------
- * _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;
*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;
}
#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);
}
/*-------------------------------------------------------------------------
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;
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,
/*---------------------------------------------------------------------------
* 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
{ /* not by value */
char *tempdata = palloc(elmlen);
- memmove(tempdata, DatumGetPointer(result), elmlen);
+ memcpy(tempdata, DatumGetPointer(result), elmlen);
result = PointerGetDatum(tempdata);
}
pfree(v);
#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;
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;
}
{
ArrayType *v;
ArrayType *result;
- char **values;
+ Datum *values;
char *elt;
int *dim;
int ndim;
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)
/* 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,
&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);
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 */
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
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);
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;
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,
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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
#endif
#include "fmgr.h"
+#include "utils/array.h"
#include "utils/builtins.h"
static void CheckFloat8Val(double val);
#ifndef atof
extern double atof(const char *p);
-
#endif
#ifndef HAVE_CBRT
#else
#if !defined(nextstep)
extern double cbrt(double x);
-
-#endif
#endif
+#endif /* HAVE_CBRT */
#ifndef HAVE_RINT
#define rint my_rint
#else
extern double rint(double x);
+#endif /* HAVE_RINT */
-#endif
-
-#endif
+#endif /* NeXT check */
/* ========== USER I/O ROUTINES ========== */
* 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)
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)
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;
-}
-
/*
* ====================
} /* 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
+ * ====================================
*/
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
*
* 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 $
*
* ----------
*/
#include <errno.h>
#include <sys/types.h>
+#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/int8.h"
#include "utils/numeric.h"
}
-/* ----------
- * 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() -
*
}
-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);
}
}
+/* ----------------------------------------------------------------------
+ *
+ * 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
*/
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;
*/
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;
*/
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;
*/
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;
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;
}
/*
* Tidy up
- *
*/
digitbuf_free(dividend.buf);
for (i = 1; i < 10; i++)
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);
{
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++;
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);
{
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)
{
}
}
+ Assert(borrow == 0); /* else caller gave us var1 < var2 */
+
while (res_ndigits > 0 && *res_digits == 0)
{
res_digits++;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "access/hash.h"
#include "access/xact.h"
#include "miscadmin.h"
+#include "utils/array.h"
#include "utils/builtins.h"
/*----------------------------------------------------------
* "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
PG_RETURN_INTERVAL_P(result);
}
-
Datum
interval_pl(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
*
*
* 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
*
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);
}
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);
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));
}
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,
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);
* 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
*
{
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
* ----------------------------------------------------------------
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200007071
+#define CATALOG_VERSION_NO 200007161
#endif
* 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
* 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;
/* ----------------
* ----------------
*/
-#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
/* ----------------
* ---------------
*/
-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 ( 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 ( 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 ( 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 ( count PGUID - int4inc - 0 0 23 23 _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 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 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 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_ ));
+
+/*
+ * 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 */
* 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
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 - - ));
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 ));
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 ));
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 */
* 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
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 - ));
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 - ));
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 - ));
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)");
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 - ));
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)");
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
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
Oid basetype;
Oid aggtype;
Node *target;
- bool usenulls;
bool aggstar;
bool aggdistinct;
int aggno;
* 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;
* 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
#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
#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)
#define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0)
#define NAME_LEN 30
-#define MAX_BUFF_SIZE BLCKSZ
typedef struct
{
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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);
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);
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);
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);
*
* 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 $
*
* ----------
*/
* 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
* 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_ */
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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'
);
-- 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
(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;
--
-- 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'
);
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
--
-- 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
-----+-------------
(0 rows)
-SELECT oid, pg_aggregate.aggtranstype1
-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
+SELECT oid, pg_aggregate.aggtranstype
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
-----+-------
(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
(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
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
-- **************** 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;
- 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.
-SELECT p1.oid, p1.aggname
-FROM pg_aggregate as p1
-WHERE p1.aggtransfn1 = 0 AND p1.aggtransfn2 = 0;
- 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));
+WHERE aggtransfn = 0 OR aggtranstype = 0 OR aggfinaltype = 0;
oid | aggname
-----+---------
(0 rows)
+-- 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 AND
- (p1.aggtranstype1 = 0 OR p1.aggtranstype2 = 0 OR
- p1.aggfinalfn = 0);
+WHERE p1.aggfinalfn = 0 AND p1.aggfinaltype != p1.aggtranstype;
oid | aggname
-----+---------
(0 rows)
--- Cross-check transfn1 (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.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.
+-- 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.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?
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)
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;
-- 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'
);
--
-- 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');
--
--
-- 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
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
-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
+SELECT oid, pg_aggregate.aggtranstype
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
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
-- **************** 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.
--- 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.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.
+-- 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.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?
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 ****************
--
-- 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 $
--
---------------------------------------------------------------------------
-----------------------------
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;