diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 91cea30605fee9a0bd6cb4c897041d31f76c9877..1613774f26be2981842625b7b2f36f30479f308a 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -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
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 44291458d60a9cfbe71ab93aa0cadd98335c9e88..07b45c112aa7ca88b8bac146501875f4687b950f 100644
--- a/doc/src/sgml/ref/create_aggregate.sgml
+++ b/doc/src/sgml/ref/create_aggregate.sgml
@@ -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>
 
diff --git a/doc/src/sgml/ref/drop_aggregate.sgml b/doc/src/sgml/ref/drop_aggregate.sgml
index 465f5d6421588cf0aed51cc4f348a105a27016f0..8efc31a732e695852baddcb3ae7fb8d2bc6495b5 100644
--- a/doc/src/sgml/ref/drop_aggregate.sgml
+++ b/doc/src/sgml/ref/drop_aggregate.sgml
@@ -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>
diff --git a/doc/src/sgml/xaggr.sgml b/doc/src/sgml/xaggr.sgml
index 8d5cb93a2d904536c5d5dbbaee7f19d17360e4e5..c1e32f9b015bbb66602755a797258002b6ade21e 100644
--- a/doc/src/sgml/xaggr.sgml
+++ b/doc/src/sgml/xaggr.sgml
@@ -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>
 
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index f068c897ec13e732d238e572a691cd37525962e6..e3fa7c5535be56ed69d40570b9b2ab1d49982548 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.34 2000/07/05 23:11:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.35 2000/07/17 03:04:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,8 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_func.h"
 #include "utils/builtins.h"
 #include "utils/syscache.h"
 
@@ -36,13 +38,7 @@
  *		Currently, redefining aggregates using the same name is not
  *		supported.	In such a case, a warning is printed that the
  *		aggregate already exists.  If such is not the case, a new tuple
- *		is created and inserted in the aggregate relation.	The fields
- *		of this tuple are aggregate name, owner id, 2 transition functions
- *		(called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
- *		type of data on which aggtransfn1 operates (aggbasetype), return
- *		types of the two transition functions (aggtranstype1 and
- *		aggtranstype2), final return type (aggfinaltype), and initial values
- *		for the two state transition functions (agginitval1 and agginitval2).
+ *		is created and inserted in the aggregate relation.
  *		All types and functions must have been defined
  *		prior to defining the aggregate.
  *
@@ -50,31 +46,27 @@
  */
 void
 AggregateCreate(char *aggName,
-				char *aggtransfn1Name,
-				char *aggtransfn2Name,
+				char *aggtransfnName,
 				char *aggfinalfnName,
 				char *aggbasetypeName,
-				char *aggtransfn1typeName,
-				char *aggtransfn2typeName,
-				char *agginitval1,
-				char *agginitval2)
+				char *aggtranstypeName,
+				char *agginitval)
 {
-	int			i;
 	Relation	aggdesc;
 	HeapTuple	tup;
 	char		nulls[Natts_pg_aggregate];
 	Datum		values[Natts_pg_aggregate];
 	Form_pg_proc proc;
-	Oid			xfn1 = InvalidOid;
-	Oid			xfn2 = InvalidOid;
-	Oid			ffn = InvalidOid;
-	Oid			xbase = InvalidOid;
-	Oid			xret1 = InvalidOid;
-	Oid			xret2 = InvalidOid;
-	Oid			fret = InvalidOid;
+	Oid			transfn;
+	Oid			finalfn = InvalidOid; /* can be omitted */
+	Oid			basetype;
+	Oid			transtype;
+	Oid			finaltype;
 	Oid			fnArgs[FUNC_MAX_ARGS];
+	int			nargs;
 	NameData	aname;
 	TupleDesc	tupDesc;
+	int			i;
 
 	MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
 
@@ -82,143 +74,112 @@ AggregateCreate(char *aggName,
 	if (!aggName)
 		elog(ERROR, "AggregateCreate: no aggregate name supplied");
 
-	if (!aggtransfn1Name && !aggtransfn2Name)
-		elog(ERROR, "AggregateCreate: aggregate must have at least one transition function");
-
-	if (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),
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index d3da6cc2b27202e766ebd934835fc527372fbc0d..b90ef61a3b0dc49f1bcd4a02ccd9b8f80ae409e9 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -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 */
 }
 
 /*
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 4698fa850c07033d2df75c3f090277ec596ff15d..deaaf51df2a1bb264a76de3526484ca917d9a43a 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -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"
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index fd9d761ffc477bf16392dbe4b496e32ce9200f7d..48cd8aa169ce44db8f69c60471b83144837a77c5 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -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"
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 40bbbd916f651a510c45f3ae55dd0be5a6fa25f4..a3f66d20cad24d39cfdf3921c742ce4d26a00279 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -12,16 +12,19 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.12 2000/07/12 02:37:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.13 2000/07/17 03:04:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include <sys/types.h>
 #include <sys/file.h>
+
 #include "postgres.h"
 
 #include "executor/executor.h"
+#include "utils/memutils.h"
+
 
 /* ----------------------------------------------------------------
  *		ExecScan
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 1ac5c3c9e21e53c88e048afaeb2d50c39ccfbf38..547a946b4ce81bf941dfb5ae41181d8dd591b0c6 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -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;
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9c2d293858ca061c0fd3261bca58b93bb8141878..f63ffe4943575c812f601beaaf8abd00bd4aa204 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -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);
 
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index e136c13154165fc6edf8c0e04af32ae214b8761b..54af882db12333b3d914b5091deab068467d99ec 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -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);
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 0186e394367849505ab660cf2254a1c44427873f..70b98a97e3a19bcdc79ac3acb8bd2f5f3d792075 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.17 2000/07/12 02:37:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.18 2000/07/17 03:04:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,10 +18,13 @@
  *		ExecInitNestLoop - initialize the join
  *		ExecEndNestLoop  - shut down the join
  */
+
 #include "postgres.h"
 
 #include "executor/execdebug.h"
 #include "executor/nodeNestloop.h"
+#include "utils/memutils.h"
+
 
 /* ----------------------------------------------------------------
  *		ExecNestLoop(node)
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index a1daaf52c4bccb31ad70f21a32ed3cc87b3ab217..770cc47ccc4f405b80e6f1083e1b35ac6c1c36f7 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -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)
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
index 929ddad5aa85cff56afc401fab53bf4682caa377..8d3af03664516475ed32c64723a4f1243423b60f 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -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> */
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index bc305382dfea192c957b90832c04a5f0044359de..4013a0f77b24d34268ce79c81fa8d075a3e13ab5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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 */
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index b8814786180158ae7951368244fd2cc7ebcd5e69..b1772e6436cd829c88b962fe9d5164dd2abcb7b8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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)
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f6fed845069c68ac23de4ef2cc23c63ed3c1e2a2..c561ad5126841f994012f5f69799570a5117638c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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 */
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 4754cbc327add461cfec06bc700cda1811013aef..b9916ce6b06ee6e8352d7448cde0a7f29a8a3780 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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;
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 600371a09f2bc27d509e62fc5895557c234b91ce..bbc8f5c70764e37c987008267136bbc772b079b8 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -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));
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index b22aff762a3155fc65613f3aefe46302b31aa518..30023453367bad8057f3a6242080a315c36fa2df 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -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)
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 12a8372c7c5d5b5b2c8c95348e253e6f41e84e4e..23a2dcf1e24e8f8f2f35e045642c17ebebe69db4 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.70 2000/06/28 03:32:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.71 2000/07/17 03:05:08 tgl Exp $
  *
  * NOTES
  *	  Outside modules can create a lock table and acquire/release
@@ -20,7 +20,7 @@
  *	Interface:
  *
  *	LockAcquire(), LockRelease(), LockMethodTableInit(),
- *	LockMethodTableRename(), LockReleaseAll, LockOwners()
+ *	LockMethodTableRename(), LockReleaseAll,
  *	LockResolveConflicts(), GrantLock()
  *
  *	NOTE: This module is used to define new lock tables.  The
@@ -35,9 +35,11 @@
 #include <signal.h>
 
 #include "postgres.h"
+
 #include "access/xact.h"
 #include "miscadmin.h"
 #include "storage/proc.h"
+#include "utils/memutils.h"
 #include "utils/ps_status.h"
 
 static int	WaitOnLock(LOCKMETHOD lockmethod, LOCK *lock, LOCKMODE lockmode);
@@ -1722,181 +1724,6 @@ nxtl:	;
 	return false;
 }
 
-#ifdef NOT_USED
-/*
- * Return an array with the pids of all processes owning a lock.
- * This works only for user locks because normal locks have no
- * pid information in the corresponding XIDLookupEnt.
- */
-ArrayType  *
-LockOwners(LOCKMETHOD lockmethod, LOCKTAG *locktag)
-{
-	XIDLookupEnt *xidLook = NULL;
-	SPINLOCK	masterLock;
-	LOCK	   *lock;
-	SHMEM_OFFSET lock_offset;
-	int			count = 0;
-	LOCKMETHODTABLE *lockMethodTable;
-	HTAB	   *xidTable;
-	bool		found;
-	int			ndims,
-				nitems,
-				hdrlen,
-				size;
-	int			lbounds[1],
-				hbounds[1];
-	ArrayType  *array;
-	int		   *data_ptr;
-
-	/* Assume that no one will modify the result */
-	static int	empty_array[] = {20, 1, 0, 0, 0};
-
-#ifdef LOCK_DEBUG
-	if (lockmethod == USER_LOCKMETHOD && Trace_userlocks)
-        elog(DEBUG, "LockOwners: user lock tag [%u]", locktag->objId.blkno);
-#endif
-
-	/* This must be changed when short term locks will be used */
-	locktag->lockmethod = lockmethod;
-
-	Assert((lockmethod >= MIN_LOCKMETHOD) && (lockmethod < NumLockMethods));
-	lockMethodTable = LockMethodTable[lockmethod];
-	if (!lockMethodTable)
-	{
-		elog(NOTICE, "lockMethodTable is null in LockOwners");
-		return (ArrayType *) &empty_array;
-	}
-
-	if (LockingIsDisabled)
-		return (ArrayType *) &empty_array;
-
-	masterLock = lockMethodTable->ctl->masterLock;
-	SpinAcquire(masterLock);
-
-	/*
-	 * Find a lock with this tag
-	 */
-	Assert(lockMethodTable->lockHash->hash == tag_hash);
-	lock = (LOCK *) hash_search(lockMethodTable->lockHash, (Pointer) locktag,
-								HASH_FIND, &found);
-
-	/*
-	 * let the caller print its own error message, too. Do not elog(WARN).
-	 */
-	if (!lock)
-	{
-		SpinRelease(masterLock);
-		elog(NOTICE, "LockOwners: locktable corrupted");
-		return (ArrayType *) &empty_array;
-	}
-
-	if (!found)
-	{
-		SpinRelease(masterLock);
-        elog(NOTICE, "LockOwners: no such lock");
-		return (ArrayType *) &empty_array;
-	}
-	LOCK_PRINT("LockOwners: found", lock, 0);
-	Assert((lock->nHolding > 0) && (lock->nActive > 0));
-	Assert(lock->nActive <= lock->nHolding);
-	lock_offset = MAKE_OFFSET(lock);
-
-	/* Construct a 1-dimensional array */
-	ndims = 1;
-	hdrlen = ARR_OVERHEAD(ndims);
-	lbounds[0] = 0;
-	hbounds[0] = lock->nActive;
-	size = hdrlen + sizeof(int) * hbounds[0];
-	array = (ArrayType *) palloc(size);
-	MemSet(array, 0, size);
-	memmove((char *) array, (char *) &size, sizeof(int));
-	memmove((char *) ARR_NDIM_PTR(array), (char *) &ndims, sizeof(int));
-	memmove((char *) ARR_DIMS(array), (char *) hbounds, ndims * sizeof(int));
-	memmove((char *) ARR_LBOUND(array), (char *) lbounds, ndims * sizeof(int));
-	SET_LO_FLAG(false, array);
-	data_ptr = (int *) ARR_DATA_PTR(array);
-
-	xidTable = lockMethodTable->xidHash;
-	hash_seq(NULL);
-	nitems = 0;
-	while ((xidLook = (XIDLookupEnt *) hash_seq(xidTable)) &&
-		   (xidLook != (XIDLookupEnt *) TRUE))
-	{
-		if (count++ > 1000)
-		{
-			elog(NOTICE, "LockOwners: possible loop, giving up");
-			break;
-		}
-
-		if (xidLook->tag.pid == 0)
-		{
-			XID_PRINT("LockOwners: no pid", xidLook);
-			continue;
-		}
-
-		if (!xidLook->tag.lock)
-		{
-			XID_PRINT("LockOwners: NULL LOCK", xidLook);
-			continue;
-		}
-
-		if (xidLook->tag.lock != lock_offset)
-		{
-			XID_PRINT("LockOwners: different lock", xidLook);
-			continue;
-		}
-
-		if (LOCK_LOCKMETHOD(*lock) != lockmethod)
-		{
-			XID_PRINT("LockOwners: other table", xidLook);
-			continue;
-		}
-
-		if (xidLook->nHolding <= 0)
-		{
-			XID_PRINT("LockOwners: not holding", xidLook);
-			continue;
-		}
-
-		if (nitems >= hbounds[0])
-		{
-			elog(NOTICE, "LockOwners: array size exceeded");
-			break;
-		}
-
-		/*
-		 * Check that the holding process is still alive by sending him an
-		 * unused (ignored) signal. If the kill fails the process is not
-		 * alive.
-		 */
-		if ((xidLook->tag.pid != MyProcPid) \
-			&&(kill(xidLook->tag.pid, SIGCHLD)) != 0)
-		{
-			/* Return a negative pid to signal that process is dead */
-			data_ptr[nitems++] = -(xidLook->tag.pid);
-			XID_PRINT("LockOwners: not alive", xidLook);
-			/* XXX - TODO: remove this entry and update lock stats */
-			continue;
-		}
-
-		/* Found a process holding the lock */
-		XID_PRINT("LockOwners: holding", xidLook);
-		data_ptr[nitems++] = xidLook->tag.pid;
-	}
-
-	SpinRelease(masterLock);
-
-	/* Adjust the actual size of the array */
-	hbounds[0] = nitems;
-	size = hdrlen + sizeof(int) * hbounds[0];
-	memmove((char *) array, (char *) &size, sizeof(int));
-	memmove((char *) ARR_DIMS(array), (char *) hbounds, ndims * sizeof(int));
-
-	return array;
-}
-
-#endif /* NOT_USED */
-
 #ifdef LOCK_DEBUG
 /*
  * Dump all locks in the proc->lockQueue. Must have already acquired
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 5baf6935d0a0d8e85ffc1897b0d660cd2fece8ea..2207af4fa19f932cff04a583d5eefa23446c6c18 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -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
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index fce4e2cc28347a5905b2b014699a33c94487ceb6..e87492fe5536c6331c568e90f5af95ef3ec55c5e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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");
 	}
 
 	/*
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 57818afb9edadcdebad9afd6add333f18da3a353..104a82cde3e7f584980e04af59e034d9b6c2b39c 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.36 2000/07/12 02:37:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.37 2000/07/17 03:05:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,8 +19,10 @@
 #include "executor/execdefs.h"
 #include "executor/executor.h"
 #include "tcop/pquery.h"
+#include "utils/memutils.h"
 #include "utils/ps_status.h"
 
+
 static char *CreateOperationTag(int operationType);
 
 
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 3fc0061304251770df9fd82d6bba92121130f100..907082a7268ba2b85682ca17e0183c1f80b8326d 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.60 2000/07/03 23:09:50 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.61 2000/07/17 03:05:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,7 +19,6 @@
 
 #include "catalog/catalog.h"
 #include "catalog/pg_type.h"
-#include "fmgr.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/libpq-fs.h"
 #include "storage/fd.h"
@@ -29,7 +28,8 @@
 
 #define ASSGN	 "="
 
-/* An array has the following internal structure:
+/*
+ * An array has the following internal structure:
  *	  <nbytes>		- total number of bytes
  *	  <ndim>		- number of dimensions of the array
  *	  <flags>		- bit mask of flags
@@ -38,20 +38,18 @@
  *	  <actual data> - whatever is the stored data
  */
 
-/*-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-*/
 static int	_ArrayCount(char *str, int *dim, int typdelim);
-static char *_ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
+static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
 			  FmgrInfo *inputproc, Oid typelem, int32 typmod,
 			  char typdelim, int typlen, bool typbyval,
 			  char typalign, int *nbytes);
-
 #ifdef LOARRAY
 static char *_ReadLOArray(char *str, int *nbytes, int *fd, bool *chunkFlag,
 			 int ndim, int *dim, int baseSize);
-
 #endif
-static void _CopyArrayEls(char **values, char *p, int nitems, int typlen,
-			  char typalign, bool typbyval);
+static void CopyArrayEls(char *p, Datum *values, int nitems,
+						 bool typbyval, int typlen, char typalign,
+						 bool freedata);
 static void system_cache_lookup(Oid element_type, bool input, int *typlen,
 				 bool *typbyval, char *typdelim, Oid *typelem, Oid *proc,
 					char *typalign);
@@ -101,7 +99,7 @@ array_in(PG_FUNCTION_ARGS)
 	int			i,
 				nitems;
 	int32		nbytes;
-	char	   *dataPtr;
+	Datum	   *dataPtr;
 	ArrayType  *retval;
 	int			ndim,
 				dim[MAXDIM],
@@ -187,32 +185,29 @@ array_in(PG_FUNCTION_ARGS)
 		retval = (ArrayType *) palloc(sizeof(ArrayType));
 		MemSet(retval, 0, sizeof(ArrayType));
 		*(int32 *) retval = sizeof(ArrayType);
-		PG_RETURN_POINTER(retval);
+		PG_RETURN_ARRAYTYPE_P(retval);
 	}
 
 	if (*p == '{')
 	{
 		/* array not a large object */
-		dataPtr = (char *) _ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem,
-							typmod, typdelim, typlen, typbyval, typalign,
-										 &nbytes);
+		dataPtr = ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem,
+							   typmod, typdelim, typlen, typbyval, typalign,
+							   &nbytes);
 		nbytes += ARR_OVERHEAD(ndim);
 		retval = (ArrayType *) palloc(nbytes);
 		MemSet(retval, 0, nbytes);
-		memmove(retval, (char *) &nbytes, sizeof(int));
-		memmove((char *) ARR_NDIM_PTR(retval), (char *) &ndim, sizeof(int));
+		retval->size = nbytes;
+		retval->ndim = ndim;
 		SET_LO_FLAG(false, retval);
-		memmove((char *) ARR_DIMS(retval), (char *) dim, ndim * sizeof(int));
-		memmove((char *) ARR_LBOUND(retval), (char *) lBound,
-				ndim * sizeof(int));
-
-		/*
-		 * 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;
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 0b6a0db2eaac145b7314bc7e4e61a6f2dbabf431..bfa439f41565d77ac9d2d05ade49a0c6528286cc 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -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
+ *		====================================
  */
 
 /*
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index bf7758c18655c8909d25783bc573a88e1ade0b1c..7133142c0b6cbe6bbc0316c68c1c2328915f6bd1 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -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)
 {
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 5748986bbfea5324022c1687cce14e8ffdb61545..437bb69b832a6a66c2825f155a6c8dbbff800f58 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -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++;
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index b4736dd6ae8c3d50c359082a2068be92c2818c03..0730d56147350f5f482e60636ec4bffabcd257bb 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -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
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index b15840eea5e1929753da8f12acb20680119ec9fd..8e8e81feb49067cd7b7377feb3ecc28a41a9a28a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -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);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 609868dbcd607f82de54dd0a61ce1bef72dd3b68..8ad340f64bb46719342f80d465f2893ecfb9cb1f 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -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;
 
diff --git a/src/include/c.h b/src/include/c.h
index 603c5bbfe852ccea6e7f4df015bc7cd461294e15..60abde17368c87570a0652f5fe896b97b07e4db4 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -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
  * ----------------------------------------------------------------
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 0d4aec6c7ec25995cbf3bb637b2689ed6ddad1b6..80be91a70f48d87d4ed70426e4089fc4f93aa7f7 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -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
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 2350d7385655c55d8366fb0f5acf58893f8dfc57..1c061cd6687f3801cad33e6dfa51167a4a20604c 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_aggregate.h,v 1.26 2000/05/30 04:24:55 tgl Exp $
+ * $Id: pg_aggregate.h,v 1.27 2000/07/17 03:05:23 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -32,30 +32,25 @@
  *		cpp turns this into typedef struct FormData_pg_aggregate
  *
  *	aggname				name of the aggregate
- *	aggtransfn1			transition function 1
- *	aggtransfn2			transition function 2
+ *	aggowner			owner (creator) of the aggregate
+ *	aggtransfn			transition function
  *	aggfinalfn			final function
  *	aggbasetype			type of data on which aggregate operates
- *	aggtranstype1		output types for transition func 1
- *	aggtranstype2		output types for transition func 2
- *	aggfinaltype		output type for final function
- *	agginitval1			initial aggregate value
- *	agginitval2			initial value for transition state 2
+ *	aggtranstype		type of aggregate's transition (state) data
+ *	aggfinaltype		type of aggregate's final result
+ *	agginitval			initial value for transition state
  * ----------------------------------------------------------------
  */
 CATALOG(pg_aggregate)
 {
 	NameData	aggname;
 	int4		aggowner;
-	regproc		aggtransfn1;
-	regproc		aggtransfn2;
+	regproc		aggtransfn;
 	regproc		aggfinalfn;
 	Oid			aggbasetype;
-	Oid			aggtranstype1;
-	Oid			aggtranstype2;
+	Oid			aggtranstype;
 	Oid			aggfinaltype;
-	text		agginitval1;	/* VARIABLE LENGTH FIELD */
-	text		agginitval2;	/* VARIABLE LENGTH FIELD */
+	text		agginitval;		/* VARIABLE LENGTH FIELD */
 } FormData_pg_aggregate;
 
 /* ----------------
@@ -70,18 +65,15 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  * ----------------
  */
 
-#define Natts_pg_aggregate				11
+#define Natts_pg_aggregate				8
 #define Anum_pg_aggregate_aggname		1
 #define Anum_pg_aggregate_aggowner		2
-#define Anum_pg_aggregate_aggtransfn1	3
-#define Anum_pg_aggregate_aggtransfn2	4
-#define Anum_pg_aggregate_aggfinalfn	5
-#define Anum_pg_aggregate_aggbasetype	6
-#define Anum_pg_aggregate_aggtranstype1 7
-#define Anum_pg_aggregate_aggtranstype2 8
-#define Anum_pg_aggregate_aggfinaltype	9
-#define Anum_pg_aggregate_agginitval1	10
-#define Anum_pg_aggregate_agginitval2	11
+#define Anum_pg_aggregate_aggtransfn	3
+#define Anum_pg_aggregate_aggfinalfn	4
+#define Anum_pg_aggregate_aggbasetype	5
+#define Anum_pg_aggregate_aggtranstype	6
+#define Anum_pg_aggregate_aggfinaltype	7
+#define Anum_pg_aggregate_agginitval	8
 
 
 /* ----------------
@@ -89,70 +81,84 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  * ---------------
  */
 
-DATA(insert OID = 0 ( avg	PGUID int8pl	  int4inc	int84div		20	 20   23   20 _null_ 0 ));
-DATA(insert OID = 0 ( avg	PGUID int4pl	  int4inc	int4div			23	 23   23   23 _null_ 0 ));
-DATA(insert OID = 0 ( avg	PGUID int2pl	  int2inc	int2div			21	 21   21   21 _null_ 0 ));
-DATA(insert OID = 0 ( avg	PGUID float4pl	  float4inc float4div	   700	700  700  700 _null_ 0.0 ));
-DATA(insert OID = 0 ( avg	PGUID float8pl	  float8inc float8div	   701	701  701  701 _null_ 0.0 ));
-DATA(insert OID = 0 ( avg	PGUID cash_pl	  float8inc cash_div_flt8  790	790  701  790 _null_ 0.0 ));
-DATA(insert OID = 0 ( avg	PGUID interval_pl float8inc interval_div  1186 1186  701 1186 _null_ 0.0 ));
-DATA(insert OID = 0 ( avg	PGUID numeric_add numeric_inc numeric_div 1700 1700 1700 1700 _null_ 0 ));
-
-DATA(insert OID = 0 ( 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 */
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 06bb7546cd8dbb175a3f10f3a6f8345b55e6cd7f..20ea41ec0fbf9ca9ab5d35cb86900ada4085f3f0 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.76 2000/06/05 07:28:59 tgl Exp $
+ * $Id: pg_operator.h,v 1.77 2000/07/17 03:05:23 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -323,12 +323,12 @@ DATA(insert OID = 636 (  "-"	   PGUID 0 b t f  18  18  18 0 0  0 0 charmi - - ))
 DATA(insert OID = 637 (  "*"	   PGUID 0 b t f  18  18  18 0 0  0 0 charmul - - ));
 DATA(insert OID = 638 (  "/"	   PGUID 0 b t f  18  18  18 0 0  0 0 chardiv - - ));
 
-DATA(insert OID = 639 (  "~"	   PGUID 0 b t f  19  25  16 0 640	0 0 nameregexeq eqsel eqjoinsel ));
+DATA(insert OID = 639 (  "~"	   PGUID 0 b t f  19  25  16 0 640	0 0 nameregexeq regexeqsel regexeqjoinsel ));
 #define OID_NAME_REGEXEQ_OP		639
-DATA(insert OID = 640 (  "!~"	   PGUID 0 b t f  19  25  16 0 639	0 0 nameregexne neqsel neqjoinsel ));
-DATA(insert OID = 641 (  "~"	   PGUID 0 b t f  25  25  16 0 642	0 0 textregexeq eqsel eqjoinsel ));
+DATA(insert OID = 640 (  "!~"	   PGUID 0 b t f  19  25  16 0 639	0 0 nameregexne regexnesel regexnejoinsel ));
+DATA(insert OID = 641 (  "~"	   PGUID 0 b t f  25  25  16 0 642	0 0 textregexeq regexeqsel regexeqjoinsel ));
 #define OID_TEXT_REGEXEQ_OP		641
-DATA(insert OID = 642 (  "!~"	   PGUID 0 b t f  25  25  16 0 641	0 0 textregexne neqsel neqjoinsel ));
+DATA(insert OID = 642 (  "!~"	   PGUID 0 b t f  25  25  16 0 641	0 0 textregexne regexnesel regexnejoinsel ));
 DATA(insert OID = 643 (  "<>"	   PGUID 0 b t f  19  19  16 643 93 0 0 namene neqsel neqjoinsel ));
 DATA(insert OID = 654 (  "||"	   PGUID 0 b t f  25  25  25   0 0	0 0 textcat - - ));
 
@@ -449,9 +449,9 @@ DATA(insert OID =  974 (  "||"	   PGUID 0 b t f 1042 1042 1042    0  0 0 0 textc
 DATA(insert OID =  979 (  "||"	   PGUID 0 b t f 1043 1043 1043    0  0 0 0 textcat - - ));
 
 DATA(insert OID = 1054 ( "="	   PGUID 0 b t f 1042 1042	 16 1054 1057 1058 1058 bpchareq eqsel eqjoinsel ));
-DATA(insert OID = 1055 ( "~"	   PGUID 0 b t f 1042	25	 16    0 1056  0 0 textregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1055 ( "~"	   PGUID 0 b t f 1042	25	 16    0 1056  0 0 textregexeq regexeqsel regexeqjoinsel ));
 #define OID_BPCHAR_REGEXEQ_OP		1055
-DATA(insert OID = 1056 ( "!~"	   PGUID 0 b t f 1042	25	 16    0 1055  0 0 textregexne neqsel neqjoinsel ));
+DATA(insert OID = 1056 ( "!~"	   PGUID 0 b t f 1042	25	 16    0 1055  0 0 textregexne regexnesel regexnejoinsel ));
 DATA(insert OID = 1057 ( "<>"	   PGUID 0 b t f 1042 1042	 16 1057 1054  0 0 bpcharne neqsel neqjoinsel ));
 DATA(insert OID = 1058 ( "<"	   PGUID 0 b t f 1042 1042	 16 1060 1061  0 0 bpcharlt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1059 ( "<="	   PGUID 0 b t f 1042 1042	 16 1061 1060  0 0 bpcharle scalarltsel scalarltjoinsel ));
@@ -459,9 +459,9 @@ DATA(insert OID = 1060 ( ">"	   PGUID 0 b t f 1042 1042	 16 1058 1059  0 0 bpcha
 DATA(insert OID = 1061 ( ">="	   PGUID 0 b t f 1042 1042	 16 1059 1058  0 0 bpcharge scalargtsel scalargtjoinsel ));
 
 DATA(insert OID = 1062 ( "="	   PGUID 0 b t t 1043 1043	16	1062 1065 1066 1066 varchareq eqsel eqjoinsel ));
-DATA(insert OID = 1063 ( "~"	   PGUID 0 b t f 1043	25	16 0 1064  0 0 textregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1063 ( "~"	   PGUID 0 b t f 1043	25	16 0 1064  0 0 textregexeq regexeqsel regexeqjoinsel ));
 #define OID_VARCHAR_REGEXEQ_OP		1063
-DATA(insert OID = 1064 ( "!~"	   PGUID 0 b t f 1043	25	16 0 1063  0 0 textregexne neqsel neqjoinsel ));
+DATA(insert OID = 1064 ( "!~"	   PGUID 0 b t f 1043	25	16 0 1063  0 0 textregexne regexnesel regexnejoinsel ));
 DATA(insert OID = 1065 ( "<>"	   PGUID 0 b t f 1043 1043	16 1065 1062  0 0 varcharne neqsel neqjoinsel ));
 DATA(insert OID = 1066 ( "<"	   PGUID 0 b t f 1043 1043	16 1068 1069  0 0 varcharlt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1067 ( "<="	   PGUID 0 b t f 1043 1043	16 1069 1068  0 0 varcharle scalarltsel scalarltjoinsel ));
@@ -527,32 +527,32 @@ DATA(insert OID = 1158 (  "!"		PGUID 0 r t f	21	  0   23 0 0 0 0 int2fac - - ));
 DATA(insert OID = 1175 (  "!!"		PGUID 0 l t f	 0	 21   23 0 0 0 0 int2fac - - ));
 
 /* LIKE hacks by Keith Parks. */
-DATA(insert OID = 1207 (  "~~"	  PGUID 0 b t f  19   25  16 0 1208 0 0 namelike eqsel eqjoinsel ));
+DATA(insert OID = 1207 (  "~~"	  PGUID 0 b t f  19   25  16 0 1208 0 0 namelike likesel likejoinsel ));
 #define OID_NAME_LIKE_OP		1207
-DATA(insert OID = 1208 (  "!~~"   PGUID 0 b t f  19   25  16 0 1207 0 0 namenlike neqsel neqjoinsel ));
-DATA(insert OID = 1209 (  "~~"	  PGUID 0 b t f  25   25  16 0 1210 0 0 textlike eqsel eqjoinsel ));
+DATA(insert OID = 1208 (  "!~~"   PGUID 0 b t f  19   25  16 0 1207 0 0 namenlike nlikesel nlikejoinsel ));
+DATA(insert OID = 1209 (  "~~"	  PGUID 0 b t f  25   25  16 0 1210 0 0 textlike likesel likejoinsel ));
 #define OID_TEXT_LIKE_OP		1209
-DATA(insert OID = 1210 (  "!~~"   PGUID 0 b t f  25   25  16 0 1209 0 0 textnlike neqsel neqjoinsel ));
-DATA(insert OID = 1211 (  "~~"	  PGUID 0 b t f  1042 25  16 0 1212 0 0 textlike eqsel eqjoinsel ));
+DATA(insert OID = 1210 (  "!~~"   PGUID 0 b t f  25   25  16 0 1209 0 0 textnlike nlikesel nlikejoinsel ));
+DATA(insert OID = 1211 (  "~~"	  PGUID 0 b t f  1042 25  16 0 1212 0 0 textlike likesel likejoinsel ));
 #define OID_BPCHAR_LIKE_OP		1211
-DATA(insert OID = 1212 (  "!~~"   PGUID 0 b t f  1042 25  16 0 1211 0 0 textnlike neqsel neqjoinsel ));
-DATA(insert OID = 1213 (  "~~"	  PGUID 0 b t f  1043 25  16 0 1214 0 0 textlike eqsel eqjoinsel ));
+DATA(insert OID = 1212 (  "!~~"   PGUID 0 b t f  1042 25  16 0 1211 0 0 textnlike nlikesel nlikejoinsel ));
+DATA(insert OID = 1213 (  "~~"	  PGUID 0 b t f  1043 25  16 0 1214 0 0 textlike likesel likejoinsel ));
 #define OID_VARCHAR_LIKE_OP		1213
-DATA(insert OID = 1214 (  "!~~"   PGUID 0 b t f  1043 25  16 0 1213 0 0 textnlike neqsel neqjoinsel ));
+DATA(insert OID = 1214 (  "!~~"   PGUID 0 b t f  1043 25  16 0 1213 0 0 textnlike nlikesel nlikejoinsel ));
 
 /* case-insensitive LIKE hacks */
-DATA(insert OID = 1226 (  "~*"		 PGUID 0 b t f	19	25	16 0 1227  0 0 nameicregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1226 (  "~*"		 PGUID 0 b t f	19	25	16 0 1227  0 0 nameicregexeq icregexeqsel icregexeqjoinsel ));
 #define OID_NAME_ICREGEXEQ_OP		1226
-DATA(insert OID = 1227 (  "!~*"		 PGUID 0 b t f	19	25	16 0 1226  0 0 nameicregexne neqsel neqjoinsel ));
-DATA(insert OID = 1228 (  "~*"		 PGUID 0 b t f	25	25	16 0 1229  0 0 texticregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1227 (  "!~*"		 PGUID 0 b t f	19	25	16 0 1226  0 0 nameicregexne icregexnesel icregexnejoinsel ));
+DATA(insert OID = 1228 (  "~*"		 PGUID 0 b t f	25	25	16 0 1229  0 0 texticregexeq icregexeqsel icregexeqjoinsel ));
 #define OID_TEXT_ICREGEXEQ_OP		1228
-DATA(insert OID = 1229 (  "!~*"		 PGUID 0 b t f	25	25	16 0 1228  0 0 texticregexne neqsel neqjoinsel ));
-DATA(insert OID = 1232 (  "~*"		PGUID 0 b t f  1043  25  16 0 1233	0 0 texticregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1229 (  "!~*"		 PGUID 0 b t f	25	25	16 0 1228  0 0 texticregexne icregexnesel icregexnejoinsel ));
+DATA(insert OID = 1232 (  "~*"		PGUID 0 b t f  1043  25  16 0 1233	0 0 texticregexeq icregexeqsel icregexeqjoinsel ));
 #define OID_VARCHAR_ICREGEXEQ_OP		1232
-DATA(insert OID = 1233 ( "!~*"		PGUID 0 b t f  1043  25  16 0 1232	0 0 texticregexne neqsel neqjoinsel ));
-DATA(insert OID = 1234 (  "~*"		PGUID 0 b t f  1042  25  16 0 1235	0 0 texticregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1233 ( "!~*"		PGUID 0 b t f  1043  25  16 0 1232	0 0 texticregexne icregexnesel icregexnejoinsel ));
+DATA(insert OID = 1234 (  "~*"		PGUID 0 b t f  1042  25  16 0 1235	0 0 texticregexeq icregexeqsel icregexeqjoinsel ));
 #define OID_BPCHAR_ICREGEXEQ_OP		1234
-DATA(insert OID = 1235 ( "!~*"		PGUID 0 b t f  1042  25  16 0 1234	0 0 texticregexne neqsel neqjoinsel ));
+DATA(insert OID = 1235 ( "!~*"		PGUID 0 b t f  1042  25  16 0 1234	0 0 texticregexne icregexnesel icregexnejoinsel ));
 
 /* timestamp operators */
 /* name, owner, prec, kind, isleft, canhash, left, right, result, com, negate, lsortop, rsortop, oprcode, operrest, oprjoin */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 538299773d96afc37d658779ff64758b926b9f50..c92ce065fb38d2b8609e47d3bee66726ab4beaa5 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -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
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 6f675873cc8558a4c34c832f6568452dc5566916..2cf59ca50c21641c134ce31be436f9337990a1ae 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -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;
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 7ec383abe7d51300539242b798f983053304155d..195842811afd8eb60a4524986099a97cc1a29f72 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -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;
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 4fb296671d77f162ded89999597bd9082c745f84..4d915e0665fef49eb8172ddd92a3f889364fd309 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: array.h,v 1.25 2000/06/13 07:35:30 tgl Exp $
+ * $Id: array.h,v 1.26 2000/07/17 03:05:32 tgl Exp $
  *
  * NOTES
  *	  XXX the data array should be MAXALIGN'd -- notice that the array
@@ -24,15 +24,27 @@
 #define ARRAY_H
 
 #include "fmgr.h"
-#include "utils/memutils.h"
 
+/*
+ * Arrays are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ */
 typedef struct
 {
-	int			size;			/* total array size (in bytes) */
+	int32		size;			/* total array size (varlena requirement) */
 	int			ndim;			/* # of dimensions */
 	int			flags;			/* implementation flags */
 } ArrayType;
 
+/*
+ * fmgr macros for array objects
+ */
+#define DatumGetArrayTypeP(X)         ((ArrayType *) PG_DETOAST_DATUM(X))
+#define DatumGetArrayTypePCopy(X)     ((ArrayType *) PG_DETOAST_DATUM_COPY(X))
+#define PG_GETARG_ARRAYTYPE_P(n)      DatumGetArrayTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_ARRAYTYPE_P_COPY(n) DatumGetArrayTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_ARRAYTYPE_P(x)      PG_RETURN_POINTER(x)
+
 /*
  * bitmask of ArrayType flags field:
  * 1st bit - large object flag
@@ -43,11 +55,9 @@ typedef struct
 #define ARR_CHK_FLAG	(0x2)
 #define ARR_OBJ_MASK	(0x1c)
 
-#define ARR_FLAGS(a)			((ArrayType *) a)->flags
 #define ARR_SIZE(a)				(((ArrayType *) a)->size)
-
 #define ARR_NDIM(a)				(((ArrayType *) a)->ndim)
-#define ARR_NDIM_PTR(a)			(&(((ArrayType *) a)->ndim))
+#define ARR_FLAGS(a)			(((ArrayType *) a)->flags)
 
 #define ARR_IS_LO(a) \
 		(((ArrayType *) a)->flags & ARR_LOB_FLAG)
@@ -102,7 +112,6 @@ typedef struct
 #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)
 
 #define NAME_LEN	30
-#define MAX_BUFF_SIZE BLCKSZ
 
 typedef struct
 {
@@ -134,6 +143,12 @@ extern ArrayType *array_assgn(ArrayType *array, int nSubscripts,
 							  bool elmbyval, int elmlen, bool *isNull);
 extern Datum array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType);
 
+extern ArrayType *construct_array(Datum *elems, int nelems,
+								  bool elmbyval, int elmlen, char elmalign);
+extern void deconstruct_array(ArrayType *array,
+							  bool elmbyval, int elmlen, char elmalign,
+							  Datum **elemsp, int *nelemsp);
+
 extern int _LOtransfer(char **destfd, int size, int nitems, char **srcfd,
 			int isSrcLO, int isDestLO);
 extern char *_array_newLO(int *fd, int flag);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 07e732f1e8645c25618c1d269c9a5f34479213ed..db772d6ecf2b69538fa88f20b192893127defa4d 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -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);
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 1a0dd692dbd7dea35546bfc1914113c67dfbe9a0..8e6412eabeeff7e863abb2cd103d6bb5cf4b3897 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -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_ */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index 61db8e7d2064d4b107ed7b081e8e66ef23f8d841..b848c894a0414b86bb26119fc835bc79f4f6136a 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: timestamp.h,v 1.8 2000/06/19 03:54:48 tgl Exp $
+ * $Id: timestamp.h,v 1.9 2000/07/17 03:05:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -171,6 +171,8 @@ extern Datum interval_mi(PG_FUNCTION_ARGS);
 extern Datum interval_mul(PG_FUNCTION_ARGS);
 extern Datum mul_d_interval(PG_FUNCTION_ARGS);
 extern Datum interval_div(PG_FUNCTION_ARGS);
+extern Datum interval_accum(PG_FUNCTION_ARGS);
+extern Datum interval_avg(PG_FUNCTION_ARGS);
 
 extern Datum timestamp_mi(PG_FUNCTION_ARGS);
 extern Datum timestamp_pl_span(PG_FUNCTION_ARGS);
diff --git a/src/pl/tcl/test/test_setup.sql b/src/pl/tcl/test/test_setup.sql
index fe71584e1a1ceb13322ff8460fec4a055745fdac..7faabc129613df677e82c021aa5eb9c1d17c3f4b 100644
--- a/src/pl/tcl/test/test_setup.sql
+++ b/src/pl/tcl/test/test_setup.sql
@@ -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'
 	);
 
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 92936e3414a6df44e65eb7dfd9668d3c871ffb5f..aaee72c01abdacc6520ba49a573041799875d226 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -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;
diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out
index d7e46fffac58a40a37539ef653e6038ac530d989..ef83c886a11454511cff793b2458e83fde3263f3 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -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'
 );
diff --git a/src/test/regress/expected/errors.out b/src/test/regress/expected/errors.out
index 8332d4d89499e43bd63e56e2ce6a4e0678243fc3..66a5c3a5c8d202546ff5b22b431afb7638080df7 100644
--- a/src/test/regress/expected/errors.out
+++ b/src/test/regress/expected/errors.out
@@ -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
  
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index a42d3d89f02747434ab61441915c6fce983bd677..a2b0ad9e3e722827117e707ae7033a210771e9b4 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -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 
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 893b4bca83f76367f5cec08eb267b65c174cc2e6..70f0134693e39de6eaa842826cc18646ed409157 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -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)
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 45b8dd990fcef7df99bb9a635756f58f76b42b04..33c8fb5f35155113d2be664c0fac6ac5b0d71070 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -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;
 
diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql
index 046f945738b8008a6b2cb96cc40be237eafe92ee..5d42ed057e3d9a9fb7c9d3f44bae4846d2579176 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -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'
 );
 
diff --git a/src/test/regress/sql/errors.sql b/src/test/regress/sql/errors.sql
index ffcb5fcdb7428dafb5a8e4f44bb7983f61e24d95..86c4e4fe60f0d45787ebf934da15ce1d59708b48 100644
--- a/src/test/regress/sql/errors.sql
+++ b/src/test/regress/sql/errors.sql
@@ -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');
 
 
 --
diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql
index 7c6b4d99efb2c81acb11f3eb4d92c4c1a49e1bcc..b7ea1f63eaa8268d3583a670e9f3985619be0453 100644
--- a/src/test/regress/sql/oidjoins.sql
+++ b/src/test/regress/sql/oidjoins.sql
@@ -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 
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 40fa91bf46ce108f10e985f8372ec7f1422516fb..d655e02eb89c70cb656ba242fc32b3a2d9a090c3 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -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 ****************
 
diff --git a/src/tutorial/complex.source b/src/tutorial/complex.source
index 361007ced9db65df2ca681967f560ebaa490a42d..e0859a67976bc96528fdfb6617f2b13c2c658d6b 100644
--- a/src/tutorial/complex.source
+++ b/src/tutorial/complex.source
@@ -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;