Revise aggregate functions per earlier discussions in pghackers.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 17 Jul 2000 03:05:41 +0000 (03:05 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 17 Jul 2000 03:05:41 +0000 (03:05 +0000)
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...

55 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/ref/create_aggregate.sgml
doc/src/sgml/ref/drop_aggregate.sgml
doc/src/sgml/xaggr.sgml
src/backend/catalog/pg_aggregate.c
src/backend/commands/define.c
src/backend/commands/user.c
src/backend/executor/execQual.c
src/backend/executor/execScan.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeNestloop.c
src/backend/executor/nodeResult.c
src/backend/libpq/be-fsstubs.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/parser/parse_agg.c
src/backend/postmaster/postmaster.c
src/backend/storage/lmgr/lock.c
src/backend/storage/smgr/md.c
src/backend/tcop/postgres.c
src/backend/tcop/pquery.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/float.c
src/backend/utils/adt/int.c
src/backend/utils/adt/numeric.c
src/backend/utils/adt/timestamp.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/include/c.h
src/include/catalog/catversion.h
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_proc.h
src/include/nodes/primnodes.h
src/include/storage/lock.h
src/include/utils/array.h
src/include/utils/builtins.h
src/include/utils/numeric.h
src/include/utils/timestamp.h
src/pl/tcl/test/test_setup.sql
src/test/regress/expected/aggregates.out
src/test/regress/expected/create_aggregate.out
src/test/regress/expected/errors.out
src/test/regress/expected/oidjoins.out
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/aggregates.sql
src/test/regress/sql/create_aggregate.sql
src/test/regress/sql/errors.sql
src/test/regress/sql/oidjoins.sql
src/test/regress/sql/opr_sanity.sql
src/tutorial/complex.source

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