diff --git a/doc/src/sgml/array.sgml b/doc/src/sgml/array.sgml
index 3901ef4efc6ceb2c7541d6f7bbded2f5bc4fe82f..a7a05762de3cb50e25f615856f02a2c3c89281a4 100644
--- a/doc/src/sgml/array.sgml
+++ b/doc/src/sgml/array.sgml
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.25 2003/03/13 01:30:26 petere Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.26 2003/06/24 23:14:42 momjian Exp $ -->
 
 <sect1 id="arrays">
  <title>Arrays</title>
@@ -60,14 +60,74 @@ INSERT INTO sal_emp
 </programlisting>
  </para>
 
+ <para>
+  A limitation of the present array implementation is that individual
+  elements of an array cannot be SQL null values.  The entire array can be set
+  to null, but you can't have an array with some elements null and some
+  not.
+ </para>
+ <para>
+  This can lead to surprising results. For example, the result of the
+  previous two inserts looks like this:
+<programlisting>
+SELECT * FROM sal_emp;
+ name  |      pay_by_quarter       |      schedule
+-------+---------------------------+--------------------
+ Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+ Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+(2 rows)
+</programlisting>
+  Because the <literal>[2][2]</literal> element of
+  <structfield>schedule</structfield> is missing in each of the
+  <command>INSERT</command> statements, the <literal>[1][2]</literal>
+  element is discarded.
+ </para>
+
  <note>
   <para>
-   A limitation of the present array implementation is that individual
-   elements of an array cannot be SQL null values.  The entire array can be set
-   to null, but you can't have an array with some elements null and some
-   not.  Fixing this is on the to-do list.
+   Fixing this is on the to-do list.
   </para>
  </note>
+
+ <para>
+  The <command>ARRAY</command> expression syntax may also be used:
+<programlisting>
+INSERT INTO sal_emp
+    VALUES ('Bill',
+    ARRAY[10000, 10000, 10000, 10000],
+    ARRAY[['meeting', 'lunch'], ['','']]);
+
+INSERT INTO sal_emp
+    VALUES ('Carol',
+    ARRAY[20000, 25000, 25000, 25000],
+    ARRAY[['talk', 'consult'], ['meeting', '']]);
+SELECT * FROM sal_emp;
+ name  |      pay_by_quarter       |           schedule
+-------+---------------------------+-------------------------------
+ Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+ Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+(2 rows)
+</programlisting>
+  Note that with this syntax, multidimensional arrays must have matching
+  extents for each dimension. This eliminates the missing-array-elements
+  problem above. For example:
+<programlisting>
+INSERT INTO sal_emp
+    VALUES ('Carol',
+    ARRAY[20000, 25000, 25000, 25000],
+    ARRAY[['talk', 'consult'], ['meeting']]);
+ERROR:  Multidimensional arrays must have array expressions with matching dimensions
+</programlisting>
+  Also notice that string literals are single quoted instead of double quoted.
+ </para>
+
+ <note>
+  <para>
+   The examples in the rest of this section are based on the
+   <command>ARRAY</command> expression syntax <command>INSERT</command>s.
+  </para>
+ </note>
+
  </sect2>
 
  <sect2>
@@ -132,11 +192,30 @@ SELECT schedule[1:2][1] FROM sal_emp WHERE name = 'Bill';
 </programlisting>
 
   with the same result.  An array subscripting operation is always taken to
-  represent an array slice if any of the subscripts are written in the
-  form
+  represent an array slice if any of the subscripts are written in the form
   <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
   A lower bound of 1 is assumed for any subscript where only one value
-  is specified.
+  is specified; another example follows:
+<programlisting>
+SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
+         schedule
+---------------------------
+ {{meeting,lunch},{"",""}}
+(1 row)
+</programlisting>
+ </para>
+
+ <para>
+  Additionally, we can also access a single arbitrary array element of 
+  a one-dimensional array with the <function>array_subscript</function>
+  function:
+<programlisting>
+SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
+ array_subscript
+-----------------
+           10000
+(1 row)
+</programlisting>
  </para>
 
  <para>
@@ -147,7 +226,23 @@ UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}'
     WHERE name = 'Carol';
 </programlisting>
 
-  or updated at a single element:
+  or using the <command>ARRAY</command> expression syntax:
+
+<programlisting>
+UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
+    WHERE name = 'Carol';
+</programlisting>
+
+  <note>
+   <para>
+    Anywhere you can use the <quote>curly braces</quote> array syntax,
+    you can also use the <command>ARRAY</command> expression syntax. The
+    remainder of this section will illustrate only one or the other, but
+    not both.
+   </para>
+  </note>
+
+  An array may also be updated at a single element:
 
 <programlisting>
 UPDATE sal_emp SET pay_by_quarter[4] = 15000
@@ -160,6 +255,14 @@ UPDATE sal_emp SET pay_by_quarter[4] = 15000
 UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
     WHERE name = 'Carol';
 </programlisting>
+
+  A one-dimensional array may also be updated with the
+  <function>array_assign</function> function:
+
+<programlisting>
+UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+    WHERE name = 'Bill';
+</programListing>
  </para>
 
  <para>
@@ -178,6 +281,88 @@ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
   create an array with subscript values running from -2 to 7.
  </para>
 
+ <para>
+  An array can also be enlarged by using the concatenation operator,
+  <command>||</command>.
+<programlisting>
+SELECT ARRAY[1,2] || ARRAY[3,4];
+   ?column?
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+      ?column?
+---------------------
+ {{5,6},{1,2},{3,4}}
+(1 row)
+</programlisting>
+
+  The concatenation operator allows a single element to be pushed on to the
+  beginning or end of a one-dimensional array. It also allows two
+  <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
+  and an <replaceable>N+1</>-dimensional array. In the former case, the two
+  <replaceable>N</>-dimension arrays become outer elements of an
+  <replaceable>N+1</>-dimensional array. In the latter, the
+  <replaceable>N</>-dimensional array is added as either the first or last
+  outer element of the <replaceable>N+1</>-dimensional array.
+
+  The array is extended in the direction of the push. Hence, by pushing
+  onto the beginning of an array with a one-based subscript, a zero-based
+  subscript array is created:
+
+<programlisting>
+SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
+ array_dims
+------------
+ [0:2]
+(1 row)
+</programlisting>
+ </para>
+
+ <para>
+  An array can also be enlarged by using the functions
+  <function>array_prepend</function>, <function>array_append</function>,
+  or <function>array_cat</function>. The first two only support one-dimensional
+  arrays, but <function>array_cat</function> supports multidimensional arrays.
+
+  Note that the concatenation operator discussed above is preferred over
+  direct use of these functions. In fact, the functions are primarily for use
+  in implementing the concatenation operator. However, they may be directly
+  useful in the creation of user-defined aggregates. Some examples:
+
+<programlisting>
+SELECT array_prepend(1, ARRAY[2,3]);
+ array_prepend
+---------------
+ {1,2,3}
+(1 row)
+
+SELECT array_append(ARRAY[1,2], 3);
+ array_append
+--------------
+ {1,2,3}
+(1 row)
+
+SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+   array_cat
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+      array_cat
+---------------------
+ {{1,2},{3,4},{5,6}}
+(1 row)
+
+SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+      array_cat
+---------------------
+ {{5,6},{1,2},{3,4}}
+</programlisting>
+ </para>
+
  <para>
   The syntax for <command>CREATE TABLE</command> allows fixed-length
   arrays to be defined:
@@ -193,6 +378,16 @@ CREATE TABLE tictactoe (
   length.
  </para>
 
+ <para>
+  An alternative syntax for one-dimensional arrays may be used.
+  <structfield>pay_by_quarter</structfield> could have been defined as:
+<programlisting>
+    pay_by_quarter  integer ARRAY[4],
+</programlisting>
+  This syntax may <emphasis>only</emphasis> be used with the integer
+  constant to denote the array size.
+ </para>
+
  <para>
   Actually, the current implementation does not enforce the declared
   number of dimensions either.  Arrays of a particular element type are
@@ -300,6 +495,72 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
    is not ignored, however: after skipping leading whitespace, everything
    up to the next right brace or delimiter is taken as the item value.
   </para>
+
+  <para>
+   As illustrated earlier in this chapter, arrays may also be represented
+   using the <command>ARRAY</command> expression syntax. This representation
+   of an array value consists of items that are interpreted according to the
+   I/O conversion rules for the array's element type, plus decoration that
+   indicates the array structure. The decoration consists of the keyword
+   <command>ARRAY</command> and square brackets (<literal>[</> and
+   <literal>]</>) around the array values, plus delimiter characters between
+   adjacent items. The delimiter character is always a comma (<literal>,</>).
+   When representing multidimensional arrays, the keyword
+   <command>ARRAY</command> is only necessary for the outer level. For example,
+   <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+<programlisting>
+SELECT ARRAY[['hello world', 'happy birthday']];
+               array
+------------------------------------
+ {{"hello world","happy birthday"}}
+(1 row)
+</programlisting>
+  or it also could be written as:
+<programlisting>
+SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+               array
+------------------------------------
+ {{"hello world","happy birthday"}}
+(1 row)
+</programlisting>
+  </para>
+
+  <para>
+   A final method to represent an array, is through an
+   <command>ARRAY</command> sub-select expression. For example:
+<programlisting>
+SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                          ?column?
+-------------------------------------------------------------
+ {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+(1 row)
+</programlisting>
+  The sub-select may <emphasis>only</emphasis> return a single column. The
+  resulting one-dimensional array will have an element for each row in the
+  sub-select result, with an element type matching that of the sub-select's
+  target column.
+  </para>
+
+  <para>
+   Arrays may be cast from one type to another in similar fashion to other
+   data types:
+
+<programlisting>
+SELECT ARRAY[1,2,3]::oid[];
+  array
+---------
+ {1,2,3}
+(1 row)
+
+SELECT CAST(ARRAY[1,2,3] AS float8[]);
+  array
+---------
+ {1,2,3}
+(1 row)
+</programlisting>
+
+  </para>
+
  </sect2>
 
  <sect2>
@@ -317,6 +578,14 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
    that would otherwise be taken as array syntax or ignorable white space.
   </para>
 
+ <note>
+  <para>
+   The discussion in the preceding paragraph with respect to double quoting does
+   not pertain to the <command>ARRAY</command> expression syntax. In that case,
+   each element is quoted exactly as any other literal value of the element type.
+  </para>
+ </note>
+
   <para>
    The array output routine will put double quotes around element values
    if they are empty strings or contain curly braces, delimiter characters,
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 3519fad36e63904538d0d3b3b30519b83db3e5ed..d092cafa2da475ccc527cfb5e2bd9ff2f109faff 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.154 2003/05/05 15:08:49 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.155 2003/06/24 23:14:42 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -6962,6 +6962,203 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
 
   </sect1>
 
+ <sect1 id="functions-array">
+  <title>Array Functions</title>
+
+  <para>
+   <xref linkend="array-operators-table"> shows the operators
+   available for the <type>array</type> types.
+  </para>
+
+    <table id="array-operators-table">
+     <title><type>array</type> Operators</title>
+     <tgroup cols="4">
+      <thead>
+       <row>
+	<entry>Operator</entry>
+	<entry>Description</entry>
+	<entry>Example</entry>
+	<entry>Result</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+	<entry> <literal>=</literal> </entry>
+	<entry>equals</entry>
+	<entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+	<entry><literal>t</literal></entry>
+       </row>
+       <row>
+	<entry> <literal>||</literal> </entry>
+	<entry>array-to-array concatenation</entry>
+	<entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+	<entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+       </row>
+       <row>
+	<entry> <literal>||</literal> </entry>
+	<entry>array-to-array concatenation</entry>
+	<entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+	<entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+       </row>
+       <row>
+	<entry> <literal>||</literal> </entry>
+	<entry>element-to-array concatenation</entry>
+	<entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+	<entry><literal>{3,4,5,6}</literal></entry>
+       </row>
+       <row>
+	<entry> <literal>||</literal> </entry>
+	<entry>array-to-element concatenation</entry>
+	<entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+	<entry><literal>{4,5,6,7}</literal></entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+
+  <para>
+   <xref linkend="array-functions-table"> shows the functions
+   available for use with array types. See <xref linkend="arrays">
+   for more discussion and examples for the use of these functions.
+  </para>
+
+    <table id="array-functions-table">
+     <title><type>array</type> Functions</title>
+     <tgroup cols="5">
+      <thead>
+       <row>
+	<entry>Function</entry>
+	<entry>Return Type</entry>
+	<entry>Description</entry>
+	<entry>Example</entry>
+	<entry>Result</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+	<entry>
+     <literal>
+      <function>array_append</function>
+      (<type>anyarray</type>, <type>anyelement</type>)
+     </literal>
+    </entry>
+	<entry><type>anyarray</type></entry>
+	<entry>
+     append an element to the end of an array, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+	<entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+	<entry><literal>{1,2,3}</literal></entry>
+       </row>
+       <row>
+	<entry>
+     <literal>
+      <function>array_cat</function>
+      (<type>anyarray</type>, <type>anyarray</type>)
+     </literal>
+    </entry>
+	<entry><type>anyarray</type></entry>
+	<entry>
+     concatenate two arrays, returning <literal>NULL</literal>
+     for <literal>NULL</literal> inputs
+    </entry>
+	<entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+	<entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+       </row>
+       <row>
+	<entry>
+     <literal>
+      <function>array_dims</function>
+      (<type>anyarray</type>)
+     </literal>
+    </entry>
+	<entry><type>text</type></entry>
+	<entry>
+     returns a text representation of array dimension lower and upper bounds,
+     generating an ERROR for <literal>NULL</literal> inputs
+    </entry>
+	<entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+	<entry><literal>[1:2][1:3]</literal></entry>
+       </row>
+       <row>
+	<entry>
+     <literal>
+      <function>array_lower</function>
+      (<type>anyarray</type>, <type>integer</type>)
+     </literal>
+    </entry>
+	<entry><type>integer</type></entry>
+	<entry>
+     returns lower bound of the requested array dimension, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+	<entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+	<entry><literal>0</literal></entry>
+       </row>
+       <row>
+	<entry>
+     <literal>
+      <function>array_prepend</function>
+      (<type>anyelement</type>, <type>anyarray</type>)
+     </literal>
+    </entry>
+	<entry><type>anyarray</type></entry>
+	<entry>
+     append an element to the beginning of an array, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+	<entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+	<entry><literal>{1,2,3}</literal></entry>
+       </row>
+       <row>
+	<entry>
+     <literal>
+      <function>array_to_string</function>
+      (<type>anyarray</type>, <type>text</type>)
+     </literal>
+    </entry>
+	<entry><type>text</type></entry>
+	<entry>
+     concatenates array elements using provided delimiter, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+	<entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+	<entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+       </row>
+       <row>
+	<entry>
+     <literal>
+      <function>array_upper</function>
+      (<type>anyarray</type>, <type>integer</type>)
+     </literal>
+    </entry>
+	<entry><type>integer</type></entry>
+	<entry>
+     returns upper bound of the requested array dimension, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+	<entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+	<entry><literal>4</literal></entry>
+       </row>
+       <row>
+	<entry>
+     <literal>
+      <function>string_to_array</function>
+      (<type>text</type>, <type>text</type>)
+     </literal>
+    </entry>
+	<entry><type>text[]</type></entry>
+	<entry>
+     splits string into array elements using provided delimiter, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+	<entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+	<entry><literal>{1.1,2.2,3.3}</literal></entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+  </sect1>
 
  <sect1 id="functions-aggregate">
   <title>Aggregate Functions</title>
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 37794645ee58d4f9d9a15c5831fd59f42cb34335..9025862be123f6734e65b537207ea2cfd1117ae9 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.56 2002/09/18 21:35:20 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.57 2003/06/24 23:14:42 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,10 +50,16 @@ AggregateCreate(const char *aggName,
 	Oid			finalfn = InvalidOid;	/* can be omitted */
 	Oid			finaltype;
 	Oid			fnArgs[FUNC_MAX_ARGS];
-	int			nargs;
+	int			nargs_transfn;
+	int			nargs_finalfn;
 	Oid			procOid;
 	TupleDesc	tupDesc;
 	int			i;
+	Oid			rettype;
+	Oid		   *true_oid_array_transfn;
+	Oid		   *true_oid_array_finalfn;
+	bool		retset;
+	FuncDetailCode fdresult;
 	ObjectAddress myself,
 				referenced;
 
@@ -68,24 +74,49 @@ AggregateCreate(const char *aggName,
 	MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
 	fnArgs[0] = aggTransType;
 	if (aggBaseType == ANYOID)
-		nargs = 1;
+		nargs_transfn = 1;
 	else
 	{
 		fnArgs[1] = aggBaseType;
-		nargs = 2;
+		nargs_transfn = 2;
 	}
-	transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
+
+	/*
+	 * func_get_detail looks up the function in the catalogs, does
+	 * disambiguation for polymorphic functions, handles inheritance, and
+	 * returns the funcid and type and set or singleton status of the
+	 * function's return value.  it also returns the true argument types
+	 * to the function.
+	 */
+	fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
+							   &transfn, &rettype, &retset,
+							   &true_oid_array_transfn);
+
+	/* only valid case is a normal function */
+	if (fdresult != FUNCDETAIL_NORMAL)
+		func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
+
 	if (!OidIsValid(transfn))
-		func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
+		func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
+
+	/*
+	 * enforce consistency with ANYARRAY and ANYELEMENT argument
+	 * and return types, possibly modifying return type along the way
+	 */
+	rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
+													   nargs_transfn, rettype);
+
+	if (rettype != aggTransType)
+		elog(ERROR, "return type of transition function %s is not %s",
+		 NameListToString(aggtransfnName), format_type_be(aggTransType));
+
 	tup = SearchSysCache(PROCOID,
 						 ObjectIdGetDatum(transfn),
 						 0, 0, 0);
 	if (!HeapTupleIsValid(tup))
-		func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
+		func_error("AggregateCreate", aggtransfnName,
+						nargs_transfn, fnArgs, NULL);
 	proc = (Form_pg_proc) GETSTRUCT(tup);
-	if (proc->prorettype != aggTransType)
-		elog(ERROR, "return type of transition function %s is not %s",
-		 NameListToString(aggtransfnName), format_type_be(aggTransType));
 
 	/*
 	 * If the transfn is strict and the initval is NULL, make sure input
@@ -105,17 +136,26 @@ AggregateCreate(const char *aggName,
 	{
 		MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
 		fnArgs[0] = aggTransType;
-		finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
-		if (!OidIsValid(finalfn))
+		nargs_finalfn = 1;
+
+		fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
+								   &finalfn, &rettype, &retset,
+								   &true_oid_array_finalfn);
+
+		/* only valid case is a normal function */
+		if (fdresult != FUNCDETAIL_NORMAL)
 			func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
-		tup = SearchSysCache(PROCOID,
-							 ObjectIdGetDatum(finalfn),
-							 0, 0, 0);
-		if (!HeapTupleIsValid(tup))
+
+		if (!OidIsValid(finalfn))
 			func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
-		proc = (Form_pg_proc) GETSTRUCT(tup);
-		finaltype = proc->prorettype;
-		ReleaseSysCache(tup);
+
+		/*
+		 * enforce consistency with ANYARRAY and ANYELEMENT argument
+		 * and return types, possibly modifying return type along the way
+		 */
+		finaltype = enforce_generic_type_consistency(fnArgs,
+													 true_oid_array_finalfn,
+													 nargs_finalfn, rettype);
 	}
 	else
 	{
@@ -126,6 +166,27 @@ AggregateCreate(const char *aggName,
 	}
 	Assert(OidIsValid(finaltype));
 
+	/*
+	 * special disallowed cases:
+	 * 1)	if finaltype is polymorphic, basetype cannot be ANY
+	 * 2)	if finaltype is polymorphic, both args to transfn must be
+	 *		polymorphic
+	 */
+	if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+	{
+		if (aggBaseType == ANYOID)
+			elog(ERROR, "aggregate with base type ANY must have a " \
+						"non-polymorphic return type");
+
+		if (nargs_transfn > 1 && (
+			(true_oid_array_transfn[0] != ANYARRAYOID &&
+			 true_oid_array_transfn[0] != ANYELEMENTOID) ||
+			(true_oid_array_transfn[1] != ANYARRAYOID &&
+			 true_oid_array_transfn[1] != ANYELEMENTOID)))
+			elog(ERROR, "aggregate with polymorphic return type requires " \
+						"state function with both arguments polymorphic");
+	}
+
 	/*
 	 * Everything looks okay.  Try to create the pg_proc entry for the
 	 * aggregate.  (This could fail if there's already a conflicting
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 552573dc1a2e61e71aa43fbbe965a3649841b1f7..07b9862e41a56d56deb1e24927c081003600dd24 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.5 2002/09/04 20:31:14 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.6 2003/06/24 23:14:43 momjian Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -119,7 +119,9 @@ DefineAggregate(List *names, List *parameters)
 		baseTypeId = typenameTypeId(baseType);
 
 	transTypeId = typenameTypeId(transType);
-	if (get_typtype(transTypeId) == 'p')
+	if (get_typtype(transTypeId) == 'p' &&
+		transTypeId != ANYARRAYOID &&
+		transTypeId != ANYELEMENTOID)
 		elog(ERROR, "Aggregate transition datatype cannot be %s",
 			 format_type_be(transTypeId));
 
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 8dac4b2e44049a74eeddd1d2184fc87503b5164b..e729344060e363c31254c8aaed9467b7208b86cf 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.130 2003/05/28 22:32:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.131 2003/06/24 23:14:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1528,17 +1528,17 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
 			{
 				/* Check other sub-arrays are compatible */
 				if (elem_ndims != ARR_NDIM(array))
-					elog(ERROR, "Multiple dimension arrays must have array "
+					elog(ERROR, "Multidimensional arrays must have array "
 						 "expressions with matching number of dimensions");
 
 				if (memcmp(elem_dims, ARR_DIMS(array),
 						   elem_ndims * sizeof(int)) != 0)
-					elog(ERROR, "Multiple dimension arrays must have array "
+					elog(ERROR, "Multidimensional arrays must have array "
 						 "expressions with matching dimensions");
 
 				if (memcmp(elem_lbs, ARR_LBOUND(array),
 						   elem_ndims * sizeof(int)) != 0)
-					elog(ERROR, "Multiple dimension arrays must have array "
+					elog(ERROR, "Multidimensional arrays must have array "
 						 "expressions with matching dimensions");
 			}
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index d0dd6b31c997b9c8fb7f462a7e20894dd723d60b..dc8c1554bb0f5e3d725352ca015ce718e4ac680c 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -45,7 +45,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.107 2003/06/22 22:04:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.108 2003/06/24 23:14:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,6 +58,7 @@
 #include "executor/executor.h"
 #include "executor/nodeAgg.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
@@ -212,7 +213,7 @@ static TupleTableSlot *agg_retrieve_direct(AggState *aggstate);
 static void agg_fill_hash_table(AggState *aggstate);
 static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
 static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
-
+static Oid resolve_type(Oid type_to_resolve, Oid context_type);
 
 /*
  * Initialize all aggregates for a new group of input values.
@@ -351,14 +352,12 @@ advance_transition_function(AggState *aggstate,
 	fcinfo.context = NULL;
 	fcinfo.resultinfo = NULL;
 	fcinfo.isnull = false;
-
 	fcinfo.flinfo = &peraggstate->transfn;
 	fcinfo.nargs = 2;
 	fcinfo.arg[0] = pergroupstate->transValue;
 	fcinfo.argnull[0] = pergroupstate->transValueIsNull;
 	fcinfo.arg[1] = newVal;
 	fcinfo.argnull[1] = isNull;
-
 	newVal = FunctionCallInvoke(&fcinfo);
 
 	/*
@@ -1187,7 +1186,21 @@ ExecInitAgg(Agg *node, EState *estate)
 		AclResult	aclresult;
 		Oid			transfn_oid,
 					finalfn_oid;
+		FuncExpr   *transfnexpr,
+				   *finalfnexpr;
 		Datum		textInitVal;
+		List	   *fargs;
+		Oid			agg_rt_type;
+		Oid		   *transfn_arg_types;
+		List	   *transfn_args = NIL;
+		int			transfn_nargs;
+		Oid			transfn_ret_type;
+		Oid		   *finalfn_arg_types = NULL;
+		List	   *finalfn_args = NIL;
+		Oid			finalfn_ret_type = InvalidOid;
+		int			finalfn_nargs = 0;
+		Node	   *arg0;
+		Node	   *arg1;
 		int			i;
 
 		/* Planner should have assigned aggregate to correct level */
@@ -1238,6 +1251,166 @@ ExecInitAgg(Agg *node, EState *estate)
 						&peraggstate->transtypeLen,
 						&peraggstate->transtypeByVal);
 
+		peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
+		peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
+
+		/* get the runtime aggregate argument type */
+		fargs = aggref->args;
+		agg_rt_type = exprType((Node *) nth(0, fargs));
+
+		/* get the transition function argument and return types */
+		transfn_ret_type = get_func_rettype(transfn_oid);
+		transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
+
+		/* resolve any polymorphic types */
+		if (transfn_nargs == 2)
+		/* base type was not ANY */
+		{
+			if (transfn_arg_types[1] == ANYARRAYOID ||
+				transfn_arg_types[1] == ANYELEMENTOID)
+				transfn_arg_types[1] = agg_rt_type;
+
+			transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+														agg_rt_type);
+
+			/*
+			 * Build arg list to use on the transfn FuncExpr node. We really
+			 * only care that the node type is correct so that the transfn
+			 * can discover the actual argument types at runtime using 
+			 * get_fn_expr_argtype()
+			 */
+			arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+															-1, COERCE_DONTCARE);
+			arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
+															-1, COERCE_DONTCARE);
+			transfn_args = makeList2(arg0, arg1);
+
+			/*
+			 * the state transition function always returns the same type
+			 * as its first argument
+			 */
+			if (transfn_ret_type == ANYARRAYOID ||
+				transfn_ret_type == ANYELEMENTOID)
+				transfn_ret_type = transfn_arg_types[0];
+		}
+		else if (transfn_nargs == 1)
+		/*
+		 * base type was ANY, therefore the aggregate return type should
+		 * be non-polymorphic
+		 */
+		{
+			Oid	finaltype = get_func_rettype(aggref->aggfnoid);
+
+			/*
+			 * this should have been prevented in AggregateCreate,
+			 * but check anyway
+			 */
+			if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
+				elog(ERROR, "aggregate with base type ANY must have a " \
+							"non-polymorphic return type");
+
+			/* see if we have a final function */
+			if (OidIsValid(finalfn_oid))
+			{
+				finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+				if (finalfn_nargs != 1)
+					elog(ERROR, "final function takes unexpected number " \
+								"of arguments: %d", finalfn_nargs);
+
+				/*
+				 * final function argument is always the same as the state
+				 * function return type
+				 */
+				if (finalfn_arg_types[0] != ANYARRAYOID &&
+					finalfn_arg_types[0] != ANYELEMENTOID)
+				{
+					/* if it is not ambiguous, use it */
+					transfn_ret_type = finalfn_arg_types[0];
+				}
+				else
+				{
+					/* if it is ambiguous, try to derive it */
+					finalfn_ret_type = finaltype;
+					finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
+															finalfn_ret_type);
+					transfn_ret_type = finalfn_arg_types[0];
+				}
+			}
+			else
+				transfn_ret_type = finaltype;
+
+			transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
+														transfn_ret_type);
+
+			/*
+			 * Build arg list to use on the transfn FuncExpr node. We really
+			 * only care that the node type is correct so that the transfn
+			 * can discover the actual argument types at runtime using 
+			 * get_fn_expr_argtype()
+			 */
+			arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
+															-1, COERCE_DONTCARE);
+			transfn_args = makeList1(arg0);
+		}
+		else
+			elog(ERROR, "state transition function takes unexpected number " \
+						"of arguments: %d", transfn_nargs);
+
+		if (OidIsValid(finalfn_oid))
+		{
+			/* get the final function argument and return types */
+			if (finalfn_ret_type == InvalidOid)
+				finalfn_ret_type = get_func_rettype(finalfn_oid);
+
+			if (!finalfn_arg_types)
+			{
+				finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
+				if (finalfn_nargs != 1)
+					elog(ERROR, "final function takes unexpected number " \
+								"of arguments: %d", finalfn_nargs);
+			}
+
+			/*
+			 * final function argument is always the same as the state
+			 * function return type, which by now should have been resolved
+			 */
+			if (finalfn_arg_types[0] == ANYARRAYOID ||
+				finalfn_arg_types[0] == ANYELEMENTOID)
+				finalfn_arg_types[0] = transfn_ret_type;
+
+			/*
+			 * Build arg list to use on the finalfn FuncExpr node. We really
+			 * only care that the node type is correct so that the finalfn
+			 * can discover the actual argument type at runtime using 
+			 * get_fn_expr_argtype()
+			 */
+			arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
+															-1, COERCE_DONTCARE);
+			finalfn_args = makeList1(arg0);
+
+			finalfn_ret_type = resolve_type(finalfn_ret_type,
+												finalfn_arg_types[0]);
+		}
+
+		fmgr_info(transfn_oid, &peraggstate->transfn);
+		transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
+						  transfn_ret_type,
+						  false,			/* cannot be a set */
+						  COERCE_DONTCARE,	/* to match any user expr */
+						  transfn_args);
+		peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+
+		if (OidIsValid(finalfn_oid))
+		{
+			fmgr_info(finalfn_oid, &peraggstate->finalfn);
+			finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
+						  finalfn_ret_type,
+						  false,			/* cannot be a set */
+						  COERCE_DONTCARE,	/* to match any user expr */
+						  finalfn_args);
+			peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+		}
+
 		/*
 		 * initval is potentially null, so don't try to access it as a
 		 * struct field. Must do it the hard way with SysCacheGetAttr.
@@ -1250,14 +1423,7 @@ ExecInitAgg(Agg *node, EState *estate)
 			peraggstate->initValue = (Datum) 0;
 		else
 			peraggstate->initValue = GetAggInitVal(textInitVal,
-												   aggform->aggtranstype);
-
-		peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
-		peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
-
-		fmgr_info(transfn_oid, &peraggstate->transfn);
-		if (OidIsValid(finalfn_oid))
-			fmgr_info(finalfn_oid, &peraggstate->finalfn);
+												   transfn_arg_types[0]);
 
 		/*
 		 * If the transfn is strict and the initval is NULL, make sure
@@ -1469,3 +1635,36 @@ aggregate_dummy(PG_FUNCTION_ARGS)
 		 fcinfo->flinfo->fn_oid);
 	return (Datum) 0;			/* keep compiler quiet */
 }
+
+static Oid
+resolve_type(Oid type_to_resolve, Oid context_type)
+{
+	Oid		resolved_type;
+
+	if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
+		resolved_type = type_to_resolve;
+	else if (type_to_resolve == ANYARRAYOID)
+	/* any array */
+	{
+		Oid		context_type_arraytype = get_array_type(context_type);
+
+		if (context_type_arraytype != InvalidOid)
+			resolved_type = context_type_arraytype;
+		else
+			resolved_type = context_type;
+	}
+	else if (type_to_resolve == ANYELEMENTOID)
+	/* any element */
+	{
+		Oid		context_type_elemtype = get_element_type(context_type);
+
+		if (context_type_elemtype != InvalidOid)
+			resolved_type = context_type_elemtype;
+		else
+			resolved_type = context_type;
+	}
+	else
+		resolved_type = type_to_resolve;
+
+	return resolved_type;
+}
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 82502c985e8e659c46ef01521f80647549958b7f..62aaa7e8d439cdd3eee4cd9f1ef010a00b5bbe37 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.47 2003/06/22 22:04:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.48 2003/06/24 23:14:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,23 +28,6 @@
 #include "utils/datum.h"
 #include "utils/lsyscache.h"
 
-
-typedef struct ArrayBuildState
-{
-	MemoryContext mcontext;		/* where all the temp stuff is kept */
-	Datum	   *dvalues;		/* array of accumulated Datums */
-	/*
-	 * The allocated size of dvalues[] is always a multiple of
-	 * ARRAY_ELEMS_CHUNKSIZE
-	 */
-#define ARRAY_ELEMS_CHUNKSIZE	64
-	int			nelems;			/* number of valid Datums in dvalues[] */
-	Oid			element_type;	/* data type of the Datums */
-	int16		typlen;			/* needed info about datatype */
-	bool		typbyval;
-	char		typalign;
-} ArrayBuildState;
-
 static Datum ExecHashSubPlan(SubPlanState *node,
 							 ExprContext *econtext,
 							 bool *isNull);
@@ -54,13 +37,6 @@ static Datum ExecScanSubPlan(SubPlanState *node,
 static void buildSubPlanHash(SubPlanState *node);
 static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
 static bool tupleAllNulls(HeapTuple tuple);
-static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-										 Datum dvalue, bool disnull,
-										 Oid element_type,
-										 MemoryContext rcontext);
-static Datum makeArrayResult(ArrayBuildState *astate,
-							 MemoryContext rcontext);
-
 
 /* ----------------------------------------------------------------
  *		ExecSubPlan
@@ -224,6 +200,7 @@ ExecScanSubPlan(SubPlanState *node,
 	PlanState  *planstate = node->planstate;
 	SubLinkType subLinkType = subplan->subLinkType;
 	bool		useOr = subplan->useOr;
+	bool		isExpr = subplan->isExpr;
 	MemoryContext oldcontext;
 	TupleTableSlot *slot;
 	Datum		result;
@@ -294,6 +271,11 @@ ExecScanSubPlan(SubPlanState *node,
 		bool		rownull = false;
 		int			col = 1;
 		List	   *plst;
+		int			numelems;
+		int			elemnum;
+		Datum		dvalue;
+		Datum	   *dvalues = NULL;
+		bool		disnull;
 
 		if (subLinkType == EXISTS_SUBLINK)
 		{
@@ -331,9 +313,6 @@ ExecScanSubPlan(SubPlanState *node,
 
 		if (subLinkType == ARRAY_SUBLINK)
 		{
-			Datum	dvalue;
-			bool	disnull;
-
 			found = true;
 			/* stash away current value */
 			dvalue = heap_getattr(tup, 1, tdesc, &disnull);
@@ -351,98 +330,163 @@ ExecScanSubPlan(SubPlanState *node,
 		found = true;
 
 		/*
-		 * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
-		 * operators for columns of tuple.
+		 * When isExpr is true, we have either a scalar expression or an
+		 * array. In the former case, this is no different than the !isExpr
+		 * case. In the latter case, iterate over the elements as if they
+		 * were from multiple input tuples.
 		 */
-		plst = subplan->paramIds;
-		foreach(lst, node->exprs)
+		if (!isExpr)
+			numelems = 1;
+		else
 		{
-			ExprState  *exprstate = (ExprState *) lfirst(lst);
-			int			paramid = lfirsti(plst);
-			ParamExecData *prmdata;
-			Datum		expresult;
-			bool		expnull;
-
-			/*
-			 * Load up the Param representing this column of the sub-select.
-			 */
-			prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
-			Assert(prmdata->execPlan == NULL);
-			prmdata->value = heap_getattr(tup, col, tdesc,
-										  &(prmdata->isnull));
+			Oid		expr_typeid = tdesc->attrs[0]->atttypid;
 
-			/*
-			 * Now we can eval the combining operator for this column.
-			 */
-			expresult = ExecEvalExprSwitchContext(exprstate, econtext,
-												  &expnull, NULL);
-
-			/*
-			 * Combine the result into the row result as appropriate.
-			 */
-			if (col == 1)
+			if (expr_typeid != subplan->exprtype)
 			{
-				rowresult = expresult;
-				rownull = expnull;
+				subplan->exprtype = expr_typeid;
+				subplan->elemtype = get_element_type(expr_typeid);
+
+				if (subplan->elemtype != InvalidOid)
+					get_typlenbyvalalign(subplan->elemtype,
+										 &subplan->elmlen,
+										 &subplan->elmbyval,
+										 &subplan->elmalign);
 			}
-			else if (useOr)
+
+			/* get current value */
+			dvalue = heap_getattr(tup, 1, tdesc, &disnull);
+
+			/* XXX this will need work if/when arrays support NULL elements */
+			if (!disnull)
 			{
-				/* combine within row per OR semantics */
-				if (expnull)
-					rownull = true;
-				else if (DatumGetBool(expresult))
+				if (subplan->elemtype != InvalidOid)
 				{
-					rowresult = BoolGetDatum(true);
-					rownull = false;
-					break;		/* needn't look at any more columns */
+					ArrayType   *v = DatumGetArrayTypeP(dvalue);
+
+					deconstruct_array(v, subplan->elemtype, subplan->elmlen,
+									  subplan->elmbyval, subplan->elmalign,
+					  				  &dvalues, &numelems);
+				}
+				else
+				{
+					numelems = 1;
+					dvalues = (Datum *) palloc(numelems * sizeof(Datum));
+					dvalues[0] = dvalue;
 				}
 			}
 			else
 			{
-				/* combine within row per AND semantics */
-				if (expnull)
-					rownull = true;
-				else if (!DatumGetBool(expresult))
-				{
-					rowresult = BoolGetDatum(false);
-					rownull = false;
-					break;		/* needn't look at any more columns */
-				}
+				numelems = 1;
+				dvalues = (Datum *) palloc(numelems * sizeof(Datum));
+				dvalues[0] = (Datum) 0;
 			}
 
-			plst = lnext(plst);
-			col++;
 		}
 
-		if (subLinkType == ANY_SUBLINK)
+		for (elemnum = 0; elemnum < numelems; elemnum++)
 		{
-			/* combine across rows per OR semantics */
-			if (rownull)
-				*isNull = true;
-			else if (DatumGetBool(rowresult))
+			/*
+			 * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
+			 * operators for columns of tuple.
+			 */
+			col = 1;
+			plst = subplan->paramIds;
+			foreach(lst, node->exprs)
 			{
-				result = BoolGetDatum(true);
-				*isNull = false;
-				break;			/* needn't look at any more rows */
+				ExprState  *exprstate = (ExprState *) lfirst(lst);
+				int			paramid = lfirsti(plst);
+				ParamExecData *prmdata;
+				Datum		expresult;
+				bool		expnull;
+
+				/*
+				 * Load up the Param representing this column of the sub-select.
+				 */
+				prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
+				Assert(prmdata->execPlan == NULL);
+
+				if (!isExpr)
+					prmdata->value = heap_getattr(tup, col, tdesc,
+												  &(prmdata->isnull));
+				else
+				{
+					prmdata->value = dvalues[elemnum];
+					prmdata->isnull = disnull;
+				}
+
+				/*
+				 * Now we can eval the combining operator for this column.
+				 */
+				expresult = ExecEvalExprSwitchContext(exprstate, econtext,
+													  &expnull, NULL);
+
+				/*
+				 * Combine the result into the row result as appropriate.
+				 */
+				if (col == 1)
+				{
+					rowresult = expresult;
+					rownull = expnull;
+				}
+				else if (useOr)
+				{
+					/* combine within row per OR semantics */
+					if (expnull)
+						rownull = true;
+					else if (DatumGetBool(expresult))
+					{
+						rowresult = BoolGetDatum(true);
+						rownull = false;
+						break;		/* needn't look at any more columns */
+					}
+				}
+				else
+				{
+					/* combine within row per AND semantics */
+					if (expnull)
+						rownull = true;
+					else if (!DatumGetBool(expresult))
+					{
+						rowresult = BoolGetDatum(false);
+						rownull = false;
+						break;		/* needn't look at any more columns */
+					}
+				}
+
+				plst = lnext(plst);
+				col++;
 			}
-		}
-		else if (subLinkType == ALL_SUBLINK)
-		{
-			/* combine across rows per AND semantics */
-			if (rownull)
-				*isNull = true;
-			else if (!DatumGetBool(rowresult))
+
+			if (subLinkType == ANY_SUBLINK)
 			{
-				result = BoolGetDatum(false);
-				*isNull = false;
-				break;			/* needn't look at any more rows */
+				/* combine across rows per OR semantics */
+				if (rownull)
+					*isNull = true;
+				else if (DatumGetBool(rowresult))
+				{
+					result = BoolGetDatum(true);
+					*isNull = false;
+					break;			/* needn't look at any more rows */
+				}
+			}
+			else if (subLinkType == ALL_SUBLINK)
+			{
+				/* combine across rows per AND semantics */
+				if (rownull)
+					*isNull = true;
+				else if (!DatumGetBool(rowresult))
+				{
+					result = BoolGetDatum(false);
+					*isNull = false;
+					break;			/* needn't look at any more rows */
+				}
+			}
+			else
+			{
+				/* must be MULTIEXPR_SUBLINK */
+				result = rowresult;
+				*isNull = rownull;
 			}
-		}
-		else
-		{
-			/* must be MULTIEXPR_SUBLINK */
-			result = rowresult;
-			*isNull = rownull;
 		}
 	}
 
@@ -480,6 +524,7 @@ static void
 buildSubPlanHash(SubPlanState *node)
 {
 	SubPlan	   *subplan = (SubPlan *) node->xprstate.expr;
+	bool		isExpr = subplan->isExpr;
 	PlanState  *planstate = node->planstate;
 	int			ncols = length(node->exprs);
 	ExprContext *innerecontext = node->innerecontext;
@@ -487,6 +532,7 @@ buildSubPlanHash(SubPlanState *node)
 	MemoryContext oldcontext;
 	int			nbuckets;
 	TupleTableSlot *slot;
+	TupleTableSlot *arrslot = NULL;
 
 	Assert(subplan->subLinkType == ANY_SUBLINK);
 	Assert(!subplan->useOr);
@@ -566,43 +612,139 @@ buildSubPlanHash(SubPlanState *node)
 	{
 		HeapTuple	tup = slot->val;
 		TupleDesc	tdesc = slot->ttc_tupleDescriptor;
-		int			col = 1;
+		TupleDesc	arrtdesc = NULL;
 		List	   *plst;
 		bool		isnew;
+		int			numelems;
+		int			elemnum;
+		Datum		dvalue;
+		Datum	   *dvalues = NULL;
+		bool		disnull;
 
 		/*
-		 * Load up the Params representing the raw sub-select outputs,
-		 * then form the projection tuple to store in the hashtable.
+		 * When isExpr is true, we have either a scalar expression or an
+		 * array. In the former case, this is no different than the !isExpr
+		 * case. In the latter case, iterate over the elements as if they
+		 * were from multiple input tuples.
 		 */
-		foreach(plst, subplan->paramIds)
+		if (!isExpr)
+			numelems = 1;
+		else
 		{
-			int			paramid = lfirsti(plst);
-			ParamExecData *prmdata;
-
-			prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
-			Assert(prmdata->execPlan == NULL);
-			prmdata->value = heap_getattr(tup, col, tdesc,
-										  &(prmdata->isnull));
-			col++;
-		}
-		slot = ExecProject(node->projRight, NULL);
-		tup = slot->val;
+			Oid		expr_typeid = tdesc->attrs[0]->atttypid;
+
+			if (expr_typeid != subplan->exprtype)
+			{
+				subplan->exprtype = expr_typeid;
+				subplan->elemtype = get_element_type(expr_typeid);
+
+				if (subplan->elemtype != InvalidOid)
+					get_typlenbyvalalign(subplan->elemtype,
+										 &subplan->elmlen,
+										 &subplan->elmbyval,
+										 &subplan->elmalign);
+			}
+
+			/* get current value */
+			dvalue = heap_getattr(tup, 1, tdesc, &disnull);
+
+			if (subplan->elemtype != InvalidOid)
+			{
+				TupleTable	tupleTable;
+				ArrayType   *v = DatumGetArrayTypeP(dvalue);
+
+				arrtdesc = CreateTemplateTupleDesc(1, false);
+				TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
+															-1, 0, false);
+
+				tupleTable = ExecCreateTupleTable(1);
+				arrslot = ExecAllocTableSlot(tupleTable);
+				ExecSetSlotDescriptor(arrslot, arrtdesc, true);
+
+				/* XXX this will need work if/when arrays support NULL elements */
+				if (!disnull)
+				{
+					deconstruct_array(v, subplan->elemtype, subplan->elmlen,
+									  subplan->elmbyval, subplan->elmalign,
+					  				  &dvalues, &numelems);
+				}
+				else
+				{
+					numelems = 1;
+					dvalues = (Datum *) palloc(numelems * sizeof(Datum));
+					dvalues[0] = (Datum) 0;
+				}
+			}
+			else
+			{
+				numelems = 1;
+				dvalues = (Datum *) palloc(numelems * sizeof(Datum));
+				dvalues[0] = dvalue;
+			}
 
-		/*
-		 * If result contains any nulls, store separately or not at all.
-		 * (Since we know the projection tuple has no junk columns, we
-		 * can just look at the overall hasnull info bit, instead of
-		 * groveling through the columns.)
-		 */
-		if (HeapTupleNoNulls(tup))
-		{
-			(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
-			node->havehashrows = true;
 		}
-		else if (node->hashnulls)
+
+		for (elemnum = 0; elemnum < numelems; elemnum++)
 		{
-			(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
-			node->havenullrows = true;
+			int	col = 1;
+
+			if (!isExpr || subplan->elemtype == InvalidOid)
+			{
+				/*
+				 * Load up the Params representing the raw sub-select outputs,
+				 * then form the projection tuple to store in the hashtable.
+				 */
+				foreach(plst, subplan->paramIds)
+				{
+					int			paramid = lfirsti(plst);
+					ParamExecData *prmdata;
+
+					prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
+					Assert(prmdata->execPlan == NULL);
+
+					prmdata->value = heap_getattr(tup, col, tdesc,
+												  &(prmdata->isnull));
+
+					col++;
+				}
+				slot = ExecProject(node->projRight, NULL);
+				tup = slot->val;
+			}
+			else
+			{
+				/*
+				 * For array type expressions, we need to build up our own
+				 * tuple and slot
+				 */
+				char		nullflag;
+
+				nullflag = disnull ? 'n' : ' ';
+				tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
+				arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
+			}
+
+			/*
+			 * If result contains any nulls, store separately or not at all.
+			 * (Since we know the projection tuple has no junk columns, we
+			 * can just look at the overall hasnull info bit, instead of
+			 * groveling through the columns.)
+			 */
+			if (HeapTupleNoNulls(tup))
+			{
+				if (!isExpr)
+					(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
+				else
+					(void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
+				node->havehashrows = true;
+			}
+			else if (node->hashnulls)
+			{
+				if (!isExpr)
+					(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
+				else
+					(void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
+				node->havenullrows = true;
+			}
 		}
 
 		/*
@@ -619,6 +761,8 @@ buildSubPlanHash(SubPlanState *node)
 	 * have the potential for a double free attempt.
 	 */
 	ExecClearTuple(node->projRight->pi_slot);
+	if (arrslot)
+		ExecClearTuple(arrslot);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -1099,101 +1243,3 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
 		parent->chgParam = bms_add_member(parent->chgParam, paramid);
 	}
 }
-
-/*
- * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
- *
- *	astate is working state (NULL on first call)
- *	rcontext is where to keep working state
- */
-static ArrayBuildState *
-accumArrayResult(ArrayBuildState *astate,
-				 Datum dvalue, bool disnull,
-				 Oid element_type,
-				 MemoryContext rcontext)
-{
-	MemoryContext arr_context,
-				  oldcontext;
-
-	if (astate == NULL)
-	{
-		/* First time through --- initialize */
-
-		/* Make a temporary context to hold all the junk */
-		arr_context = AllocSetContextCreate(rcontext,
-											"ARRAY_SUBLINK Result",
-											ALLOCSET_DEFAULT_MINSIZE,
-											ALLOCSET_DEFAULT_INITSIZE,
-											ALLOCSET_DEFAULT_MAXSIZE);
-		oldcontext = MemoryContextSwitchTo(arr_context);
-		astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-		astate->mcontext = arr_context;
-		astate->dvalues = (Datum *)
-			palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-		astate->nelems = 0;
-		astate->element_type = element_type;
-		get_typlenbyvalalign(element_type,
-							 &astate->typlen,
-							 &astate->typbyval,
-							 &astate->typalign);
-	}
-	else
-	{
-		oldcontext = MemoryContextSwitchTo(astate->mcontext);
-		Assert(astate->element_type == element_type);
-		/* enlarge dvalues[] if needed */
-		if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-			astate->dvalues = (Datum *)
-				repalloc(astate->dvalues,
-						 (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-	}
-
-	if (disnull)
-		elog(ERROR, "NULL elements not allowed in Arrays");
-
-	/* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-	astate->dvalues[astate->nelems++] =
-		datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-	MemoryContextSwitchTo(oldcontext);
-
-	return astate;
-}
-
-/*
- * makeArrayResult - produce final result of ARRAY_SUBLINK
- *
- *	astate is working state (not NULL)
- *	rcontext is where to construct result
- */
-static Datum
-makeArrayResult(ArrayBuildState *astate,
-				MemoryContext rcontext)
-{
-	ArrayType  *result;
-	int			dims[1];
-	int			lbs[1];
-	MemoryContext oldcontext;
-
-	/* Build the final array result in rcontext */
-	oldcontext = MemoryContextSwitchTo(rcontext);
-
-	dims[0] = astate->nelems;
-	lbs[0] = 1;
-
-	result = construct_md_array(astate->dvalues,
-								1,
-								dims,
-								lbs,
-								astate->element_type,
-								astate->typlen,
-								astate->typbyval,
-								astate->typalign);
-
-	MemoryContextSwitchTo(oldcontext);
-
-	/* Clean up all the junk */
-	MemoryContextDelete(astate->mcontext);
-
-	return PointerGetDatum(result);
-}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 12b82fc5bcb4c3a8fbb9044780ffae797d906d57..b5e5b112725c06ba9e69db48d8b00c3e48d40542 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.252 2003/06/06 15:04:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.253 2003/06/24 23:14:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -728,6 +728,7 @@ _copyAggref(Aggref *from)
 	COPY_SCALAR_FIELD(agglevelsup);
 	COPY_SCALAR_FIELD(aggstar);
 	COPY_SCALAR_FIELD(aggdistinct);
+	COPY_NODE_FIELD(args);
 
 	return newnode;
 }
@@ -826,6 +827,7 @@ _copySubLink(SubLink *from)
 
 	COPY_SCALAR_FIELD(subLinkType);
 	COPY_SCALAR_FIELD(useOr);
+	COPY_SCALAR_FIELD(isExpr);
 	COPY_NODE_FIELD(lefthand);
 	COPY_NODE_FIELD(operName);
 	COPY_OIDLIST_FIELD(operOids);
@@ -844,6 +846,12 @@ _copySubPlan(SubPlan *from)
 
 	COPY_SCALAR_FIELD(subLinkType);
 	COPY_SCALAR_FIELD(useOr);
+	COPY_SCALAR_FIELD(isExpr);
+	COPY_SCALAR_FIELD(exprtype);
+	COPY_SCALAR_FIELD(elemtype);
+	COPY_SCALAR_FIELD(elmlen);
+	COPY_SCALAR_FIELD(elmbyval);
+	COPY_SCALAR_FIELD(elmalign);
 	COPY_NODE_FIELD(exprs);
 	COPY_INTLIST_FIELD(paramIds);
 	COPY_NODE_FIELD(plan);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 40211231c6f9b8c6a8b974551811f2accc797ec1..23c1018968d32634e272c156d662c5c8bb183015 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.195 2003/06/06 15:04:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.196 2003/06/24 23:14:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -205,6 +205,7 @@ _equalAggref(Aggref *a, Aggref *b)
 	COMPARE_SCALAR_FIELD(agglevelsup);
 	COMPARE_SCALAR_FIELD(aggstar);
 	COMPARE_SCALAR_FIELD(aggdistinct);
+	COMPARE_NODE_FIELD(args);
 
 	return true;
 }
@@ -301,6 +302,7 @@ _equalSubLink(SubLink *a, SubLink *b)
 {
 	COMPARE_SCALAR_FIELD(subLinkType);
 	COMPARE_SCALAR_FIELD(useOr);
+	COMPARE_SCALAR_FIELD(isExpr);
 	COMPARE_NODE_FIELD(lefthand);
 	COMPARE_NODE_FIELD(operName);
 	COMPARE_OIDLIST_FIELD(operOids);
@@ -314,6 +316,12 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
 {
 	COMPARE_SCALAR_FIELD(subLinkType);
 	COMPARE_SCALAR_FIELD(useOr);
+	COMPARE_SCALAR_FIELD(isExpr);
+	COMPARE_SCALAR_FIELD(exprtype);
+	COMPARE_SCALAR_FIELD(elemtype);
+	COMPARE_SCALAR_FIELD(elmlen);
+	COMPARE_SCALAR_FIELD(elmbyval);
+	COMPARE_SCALAR_FIELD(elmalign);
 	COMPARE_NODE_FIELD(exprs);
 	COMPARE_INTLIST_FIELD(paramIds);
 	/* should compare plans, but have to settle for comparing plan IDs */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f042e8a8d210333c4fcc05b7acb7be385a7f2957..cbd5784f563e92ba8c16c0aea00fb8ccb0f1cd8a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.208 2003/06/15 22:51:45 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.209 2003/06/24 23:14:43 momjian Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -616,6 +616,7 @@ _outAggref(StringInfo str, Aggref *node)
 	WRITE_UINT_FIELD(agglevelsup);
 	WRITE_BOOL_FIELD(aggstar);
 	WRITE_BOOL_FIELD(aggdistinct);
+	WRITE_NODE_FIELD(args);
 }
 
 static void
@@ -701,6 +702,7 @@ _outSubLink(StringInfo str, SubLink *node)
 
 	WRITE_ENUM_FIELD(subLinkType, SubLinkType);
 	WRITE_BOOL_FIELD(useOr);
+	WRITE_BOOL_FIELD(isExpr);
 	WRITE_NODE_FIELD(lefthand);
 	WRITE_NODE_FIELD(operName);
 	WRITE_OIDLIST_FIELD(operOids);
@@ -714,6 +716,12 @@ _outSubPlan(StringInfo str, SubPlan *node)
 
 	WRITE_ENUM_FIELD(subLinkType, SubLinkType);
 	WRITE_BOOL_FIELD(useOr);
+	WRITE_BOOL_FIELD(isExpr);
+	WRITE_OID_FIELD(exprtype);
+	WRITE_OID_FIELD(elemtype);
+	WRITE_INT_FIELD(elmlen);
+	WRITE_BOOL_FIELD(elmbyval);
+	WRITE_CHAR_FIELD(elmalign);
 	WRITE_NODE_FIELD(exprs);
 	WRITE_INTLIST_FIELD(paramIds);
 	WRITE_NODE_FIELD(plan);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 7d3a1506a81d17fb56ee2b3139e19c7b085388d0..a661f9e8ad1776bf9d99ad7a3d0b6f2cef533bd1 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.154 2003/06/06 15:04:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.155 2003/06/24 23:14:43 momjian Exp $
  *
  * NOTES
  *	  Path and Plan nodes do not have any readfuncs support, because we
@@ -416,6 +416,7 @@ _readAggref(void)
 	READ_UINT_FIELD(agglevelsup);
 	READ_BOOL_FIELD(aggstar);
 	READ_BOOL_FIELD(aggdistinct);
+	READ_NODE_FIELD(args);
 
 	READ_DONE();
 }
@@ -545,6 +546,7 @@ _readSubLink(void)
 
 	READ_ENUM_FIELD(subLinkType, SubLinkType);
 	READ_BOOL_FIELD(useOr);
+	READ_BOOL_FIELD(isExpr);
 	READ_NODE_FIELD(lefthand);
 	READ_NODE_FIELD(operName);
 	READ_OIDLIST_FIELD(operOids);
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 930c3133030487d5b36314bf4833a5211db699ea..138e7d80638ce1d80c7bfaa2ac5fb5d96fc931d8 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.76 2003/06/06 15:04:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.77 2003/06/24 23:14:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,7 +83,7 @@ typedef struct finalize_primnode_context
 
 static List *convert_sublink_opers(List *lefthand, List *operOids,
 								   List *targetlist, int rtindex,
-								   List **righthandIds);
+								   bool isExpr, List **righthandIds);
 static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
 static Node *replace_correlation_vars_mutator(Node *node, void *context);
 static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
@@ -299,6 +299,12 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
 	 */
 	node->subLinkType = slink->subLinkType;
 	node->useOr = slink->useOr;
+	node->isExpr = slink->isExpr;
+	node->exprtype = InvalidOid;
+	node->elemtype = InvalidOid;
+	node->elmlen = 0;
+	node->elmbyval = false;
+	node->elmalign = '\0';
 	node->exprs = NIL;
 	node->paramIds = NIL;
 	node->useHashTable = false;
@@ -374,7 +380,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
 		exprs = convert_sublink_opers(lefthand,
 									  slink->operOids,
 									  plan->targetlist,
-									  0,
+									  0, node->isExpr,
 									  &node->paramIds);
 		node->setParam = listCopy(node->paramIds);
 		PlannerInitPlan = lappend(PlannerInitPlan, node);
@@ -457,7 +463,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
 		node->exprs = convert_sublink_opers(lefthand,
 											slink->operOids,
 											plan->targetlist,
-											0,
+											0, node->isExpr,
 											&node->paramIds);
 
 		/*
@@ -499,7 +505,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
 static List *
 convert_sublink_opers(List *lefthand, List *operOids,
 					  List *targetlist, int rtindex,
-					  List **righthandIds)
+					  bool isExpr, List **righthandIds)
 {
 	List	   *result = NIL;
 	List	   *lst;
@@ -554,13 +560,38 @@ convert_sublink_opers(List *lefthand, List *operOids,
 		 * are not expecting to have to resolve unknown Params, so
 		 * it's okay to pass a null pstate.)
 		 */
-		result = lappend(result,
-						 make_op_expr(NULL,
-									  tup,
-									  leftop,
-									  rightop,
-									  exprType(leftop),
-									  te->resdom->restype));
+		if (!isExpr)
+		{
+			result = lappend(result,
+							 make_op_expr(NULL,
+										  tup,
+										  leftop,
+										  rightop,
+										  exprType(leftop),
+										  te->resdom->restype));
+		}
+		else
+		{
+			Oid		exprtype = te->resdom->restype;
+			Oid		elemtype = get_element_type(exprtype);
+
+			if (elemtype != InvalidOid)
+				result = lappend(result,
+								 make_op_expr(NULL,
+											  tup,
+											  leftop,
+											  rightop,
+											  exprType(leftop),
+											  elemtype));
+			else
+				result = lappend(result,
+								 make_op_expr(NULL,
+											  tup,
+											  leftop,
+											  rightop,
+											  exprType(leftop),
+											  exprtype));
+		}
 
 		ReleaseSysCache(tup);
 
@@ -671,13 +702,17 @@ convert_IN_to_join(Query *parse, SubLink *sublink)
 	/*
 	 * The sublink type must be "= ANY" --- that is, an IN operator.
 	 * (We require the operator name to be unqualified, which may be
-	 * overly paranoid, or may not be.)
+	 * overly paranoid, or may not be.) It must not be an Expression
+	 * sublink.
 	 */
 	if (sublink->subLinkType != ANY_SUBLINK)
 		return NULL;
 	if (length(sublink->operName) != 1 ||
 		strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
 		return NULL;
+	if (sublink->isExpr)
+		return NULL;
+
 	/*
 	 * The sub-select must not refer to any Vars of the parent query.
 	 * (Vars of higher levels should be okay, though.)
@@ -730,7 +765,7 @@ convert_IN_to_join(Query *parse, SubLink *sublink)
 	exprs = convert_sublink_opers(sublink->lefthand,
 								  sublink->operOids,
 								  subselect->targetList,
-								  rtindex,
+								  rtindex, sublink->isExpr,
 								  &ininfo->sub_targetlist);
 	return (Node *) make_ands_explicit(exprs);
 }
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 3f4ced5a5532a1b959e46dff79e751a7f20d8b9d..6b5014f90e1b630d693000dabe1cb3bb29bdfa0d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.139 2003/06/06 15:04:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.140 2003/06/24 23:14:43 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -132,6 +132,28 @@ get_rightop(Expr *clause)
 		return NULL;
 }
 
+/*****************************************************************************
+ *              FUNCTION clause functions
+ *****************************************************************************/
+
+/*
+ * make_funcclause
+ *		Creates a function clause given its function info and argument list.
+ */
+Expr *
+make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+							CoercionForm funcformat, List *funcargs)
+{
+	FuncExpr   *expr = makeNode(FuncExpr);
+
+	expr->funcid = funcid;
+	expr->funcresulttype = funcresulttype;
+	expr->funcretset = funcretset;
+	expr->funcformat = funcformat;
+	expr->args = funcargs;
+	return (Expr *) expr;
+}
+
 /*****************************************************************************
  *		NOT clause functions
  *****************************************************************************/
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f8958d5878329309b019d60135c744951f79f97d..e4cf67eca3679ec1a07dde76db94be496d84b5f0 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.417 2003/06/17 23:12:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.418 2003/06/24 23:14:43 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -5490,6 +5490,7 @@ r_expr:  row IN_P select_with_parens
 				{
 					SubLink *n = makeNode(SubLink);
 					n->subLinkType = ANY_SUBLINK;
+					n->isExpr = false;
 					n->lefthand = $1;
 					n->operName = makeList1(makeString("="));
 					n->subselect = $3;
@@ -5500,6 +5501,7 @@ r_expr:  row IN_P select_with_parens
 					/* Make an IN node */
 					SubLink *n = makeNode(SubLink);
 					n->subLinkType = ANY_SUBLINK;
+					n->isExpr = false;
 					n->lefthand = $1;
 					n->operName = makeList1(makeString("="));
 					n->subselect = $4;
@@ -5511,6 +5513,7 @@ r_expr:  row IN_P select_with_parens
 				{
 					SubLink *n = makeNode(SubLink);
 					n->subLinkType = $3;
+					n->isExpr = false;
 					n->lefthand = $1;
 					n->operName = $2;
 					n->subselect = $4;
@@ -5521,6 +5524,7 @@ r_expr:  row IN_P select_with_parens
 				{
 					SubLink *n = makeNode(SubLink);
 					n->subLinkType = MULTIEXPR_SUBLINK;
+					n->isExpr = false;
 					n->lefthand = $1;
 					n->operName = $2;
 					n->subselect = $3;
@@ -5904,6 +5908,7 @@ a_expr:		c_expr									{ $$ = $1; }
 					{
 							SubLink *n = (SubLink *)$3;
 							n->subLinkType = ANY_SUBLINK;
+							n->isExpr = false;
 							n->lefthand = makeList1($1);
 							n->operName = makeList1(makeString("="));
 							$$ = (Node *)n;
@@ -5931,6 +5936,7 @@ a_expr:		c_expr									{ $$ = $1; }
 					{
 						/* Make an IN node */
 						SubLink *n = (SubLink *)$4;
+						n->isExpr = false;
 						n->subLinkType = ANY_SUBLINK;
 						n->lefthand = makeList1($1);
 						n->operName = makeList1(makeString("="));
@@ -5957,11 +5963,38 @@ a_expr:		c_expr									{ $$ = $1; }
 				{
 					SubLink *n = makeNode(SubLink);
 					n->subLinkType = $3;
+					n->isExpr = false;
 					n->lefthand = makeList1($1);
 					n->operName = $2;
 					n->subselect = $4;
 					$$ = (Node *)n;
 				}
+			| a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
+				{
+					SubLink *n = makeNode(SubLink);
+					SelectStmt *s = makeNode(SelectStmt);
+					ResTarget *r = makeNode(ResTarget);
+
+					r->name = NULL;
+					r->indirection = NIL;
+					r->val = (Node *)$5;
+
+					s->distinctClause = NIL;
+					s->targetList = makeList1(r);
+					s->into = NULL;
+					s->intoColNames = NIL;
+					s->fromClause = NIL;
+					s->whereClause = NULL;
+					s->groupClause = NIL;
+					s->havingClause = NULL;
+
+					n->subLinkType = $3;
+					n->isExpr = true;
+					n->lefthand = makeList1($1);
+					n->operName = $2;
+					n->subselect = (Node *) s;
+					$$ = (Node *)n;
+				}
 			| UNIQUE select_with_parens %prec Op
 				{
 					/* Not sure how to get rid of the parentheses
@@ -6538,6 +6571,7 @@ c_expr:		columnref								{ $$ = (Node *) $1; }
 				{
 					SubLink *n = makeNode(SubLink);
 					n->subLinkType = EXPR_SUBLINK;
+					n->isExpr = false;
 					n->lefthand = NIL;
 					n->operName = NIL;
 					n->subselect = $1;
@@ -6547,6 +6581,7 @@ c_expr:		columnref								{ $$ = (Node *) $1; }
 				{
 					SubLink *n = makeNode(SubLink);
 					n->subLinkType = EXISTS_SUBLINK;
+					n->isExpr = false;
 					n->lefthand = NIL;
 					n->operName = NIL;
 					n->subselect = $2;
@@ -6556,6 +6591,7 @@ c_expr:		columnref								{ $$ = (Node *) $1; }
 				{
 					SubLink *n = makeNode(SubLink);
 					n->subLinkType = ARRAY_SUBLINK;
+					n->isExpr = false;
 					n->lefthand = NIL;
 					n->operName = NIL;
 					n->subselect = $2;
@@ -6730,6 +6766,7 @@ trim_list:	a_expr FROM expr_list					{ $$ = lappend($3, $1); }
 in_expr:	select_with_parens
 				{
 					SubLink *n = makeNode(SubLink);
+					n->isExpr = false;
 					n->subselect = $1;
 					/* other fields will be filled later */
 					$$ = (Node *)n;
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index fc35c2d6d41579171680dd0bbad6f62749154662..9861d18fdb05a8dff32c7f638f567b79a33c4419 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.97 2003/05/26 00:11:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.98 2003/06/24 23:14:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -859,7 +859,11 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 	/* Get the element type based on the array type, if we have one */
 	if (OidIsValid(array_typeid))
 	{
-		array_typelem = get_element_type(array_typeid);
+		if (array_typeid != ANYARRAYOID)
+			array_typelem = get_element_type(array_typeid);
+		else
+			array_typelem = ANYELEMENTOID;
+
 		if (!OidIsValid(array_typelem))
 			elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
 				 format_type_be(array_typeid));
@@ -919,7 +923,11 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 	{
 		if (!OidIsValid(array_typeid))
 		{
-			array_typeid = get_array_type(elem_typeid);
+			if (elem_typeid != ANYELEMENTOID)
+				array_typeid = get_array_type(elem_typeid);
+			else
+				array_typeid = ANYARRAYOID;
+
 			if (!OidIsValid(array_typeid))
 				elog(ERROR, "Cannot find array type for datatype %s",
 					 format_type_be(elem_typeid));
@@ -1170,6 +1178,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
 	if (srctype == targettype)
 		return true;
 
+	/* Last of the fast-paths: check for matching polymorphic arrays */
+	if (targettype == ANYARRAYOID)
+		if (get_element_type(srctype) != InvalidOid)
+			return true;
+
 	/* Else look in pg_cast */
 	tuple = SearchSysCache(CASTSOURCETARGET,
 						   ObjectIdGetDatum(srctype),
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 429a9ac8c8a3dffe1b5c8771163008b22848972c..5234c22e4fbf8e15dee6a4e399c50bf7c4b524ab 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.148 2003/04/29 22:13:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.149 2003/06/24 23:14:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -436,6 +436,7 @@ transformExpr(ParseState *pstate, Node *expr)
 					sublink->operName = NIL;
 					sublink->operOids = NIL;
 					sublink->useOr = FALSE;
+					sublink->isExpr = FALSE;
 				}
 				else if (sublink->subLinkType == EXPR_SUBLINK ||
 						 sublink->subLinkType == ARRAY_SUBLINK)
@@ -463,6 +464,7 @@ transformExpr(ParseState *pstate, Node *expr)
 					sublink->operName = NIL;
 					sublink->operOids = NIL;
 					sublink->useOr = FALSE;
+					sublink->isExpr = FALSE;
 				}
 				else
 				{
@@ -538,10 +540,30 @@ transformExpr(ParseState *pstate, Node *expr)
 						 * here, because make_subplan() will insert type
 						 * coercion calls if needed.
 						 */
-						optup = oper(op,
-									 exprType(lexpr),
-									 exprType((Node *) tent->expr),
-									 false);
+						if (!sublink->isExpr)
+						{
+							optup = oper(op,
+										 exprType(lexpr),
+										 exprType((Node *) tent->expr),
+										 false);
+						}
+						else
+						{
+							Oid		exprtype = exprType((Node *) tent->expr);
+							Oid		elemtype = get_element_type(exprtype);
+
+							if (elemtype != InvalidOid)
+								optup = oper(op,
+											 exprType(lexpr),
+											 elemtype,
+											 false);
+							else
+								optup = oper(op,
+											 exprType(lexpr),
+											 exprtype,
+											 false);
+						}
+
 						opform = (Form_pg_operator) GETSTRUCT(optup);
 
 						if (opform->oprresult != BOOLOID)
@@ -743,7 +765,7 @@ transformExpr(ParseState *pstate, Node *expr)
 						ArrayExpr  *e = (ArrayExpr *) lfirst(element);
 
 						if (!IsA(e, ArrayExpr))
-							elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
+							elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
 						if (ndims == 0)
 							ndims = e->ndims;
 						else if (e->ndims != ndims)
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 6f858e17a8b065b1ba16a2d22a884bf4ef0df238..5ff2bc6d81973adfa4d5170d1d11eabe6534e4ae 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.149 2003/06/06 15:04:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.150 2003/06/24 23:14:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -336,6 +336,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		aggref->target = lfirst(fargs);
 		aggref->aggstar = agg_star;
 		aggref->aggdistinct = agg_distinct;
+		aggref->args = fargs;
 
 		/* parse_agg.c does additional aggregate-specific processing */
 		transformAggregateCall(pstate, aggref);
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 21f73994c42b19965f3e959c70e989d65559a8fa..a6b66625be203e53ce2373e6b33131158664eed8 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.64 2003/05/26 00:11:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.65 2003/06/24 23:14:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,6 +137,33 @@ Operator
 equality_oper(Oid argtype, bool noError)
 {
 	Operator	optup;
+	Oid			elem_type = get_element_type(argtype);
+
+	if (OidIsValid(elem_type))
+	{
+		bool	found = false;
+		/*
+		 * If the datatype is an array, look for an "=" operator for the
+		 * element datatype.  We require it to be an exact or binary-compatible
+		 * match, since most callers are not prepared to cope with adding any
+		 * run-time type coercion steps.
+		 */
+		optup = equality_oper(elem_type, true);
+		if (optup != NULL)
+		{
+			found = true;
+			ReleaseSysCache(optup);
+		}
+
+		if (!found)
+		{
+			if (!noError)
+				elog(ERROR, "Unable to identify an equality operator for " \
+							"array type's element type %s",
+							 format_type_be(elem_type));
+			return NULL;
+		}
+	}
 
 	/*
 	 * Look for an "=" operator for the datatype.  We require it to be
@@ -175,6 +202,33 @@ Operator
 ordering_oper(Oid argtype, bool noError)
 {
 	Operator	optup;
+	Oid			elem_type = get_element_type(argtype);
+
+	if (OidIsValid(elem_type))
+	{
+		bool	found = false;
+		/*
+		 * If the datatype is an array, find the array element type's equality
+		 * operator, and use its lsortop (it *must* be mergejoinable).  We use
+		 * this definition because for sorting and grouping purposes, it's
+		 * important that the equality and ordering operators are consistent.
+		 */
+		optup = ordering_oper(elem_type, true);
+		if (optup != NULL)
+		{
+			found = true;
+			ReleaseSysCache(optup);
+		}
+
+		if (!found)
+		{
+			if (!noError)
+				elog(ERROR, "Unable to identify an ordering operator for " \
+							"array type's element type %s",
+							 format_type_be(elem_type));
+			return NULL;
+		}
+	}
 
 	/*
 	 * Find the type's equality operator, and use its lsortop (it *must*
@@ -220,6 +274,21 @@ equality_oper_funcid(Oid argtype)
 	return result;
 }
 
+/*
+ * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+ */
+Oid
+ordering_oper_funcid(Oid argtype)
+{
+	Operator	optup;
+	Oid			result;
+
+	optup = ordering_oper(argtype, false);
+	result = oprfuncid(optup);
+	ReleaseSysCache(optup);
+	return result;
+}
+
 /*
  * ordering_oper_opid - convenience routine for oprid(ordering_oper())
  *
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 2870b5d0d6538680c51712f52b6a00ee87d1d75a..cfef2db349d21c9b76fdaa5264e5a583b3297194 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.88 2003/06/11 09:23:55 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.89 2003/06/24 23:14:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -427,6 +427,15 @@ aclitemeq(const AclItem *a1, const AclItem *a2)
 		a1->ai_grantor == a2->ai_grantor;
 }
 
+/*
+ * user-facing version of aclitemeq() for use as the
+ * aclitem equality operator
+ */
+Datum
+aclitem_eq(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
+}
 
 /*
  * acldefault()  --- create an ACL describing default access permissions
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 8c412675a38c007700c4055280d789eafbdfebc8..f5e50771b1337aae7dab378367da51fd2696a05d 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.1 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.2 2003/06/24 23:14:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,35 +18,6 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
-
-/*-----------------------------------------------------------------------------
- * singleton_array :
- *		Form a multi-dimensional array given one starting element.
- *
- * - first argument is the datum with which to build the array
- * - second argument is the number of dimensions the array should have;
- *     defaults to 1 if no second argument is provided
- *----------------------------------------------------------------------------
- */
-Datum
-singleton_array(PG_FUNCTION_ARGS)
-{
-	Oid			elem_type = get_fn_expr_argtype(fcinfo, 0);
-	int			ndims;
-
-	if (elem_type == InvalidOid)
-		elog(ERROR, "Cannot determine input datatype");
-
-	if (PG_NARGS() == 2)
-		ndims = PG_GETARG_INT32(1);
-	else
-		ndims = 1;
-
-	PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
-												 PG_GETARG_DATUM(0),
-												 ndims));
-}
-
 /*-----------------------------------------------------------------------------
  * array_push :
  *		push an element onto either end of a one-dimensional array
@@ -70,6 +41,7 @@ array_push(PG_FUNCTION_ARGS)
 	Oid			arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
 	Oid			arg0_elemid;
 	Oid			arg1_elemid;
+	ArrayMetaState *my_extra;
 
 	if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
 		elog(ERROR, "array_push: cannot determine input data types");
@@ -95,28 +67,61 @@ array_push(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();		/* keep compiler quiet */
 	}
 
-	/* Sanity check: do we have a one-dimensional array */
-	if (ARR_NDIM(v) != 1)
-		elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-	lb = ARR_LBOUND(v);
-	dimv = ARR_DIMS(v);
-	if (arg0_elemid != InvalidOid)
+	if (ARR_NDIM(v) == 1)
 	{
-		/* append newelem */
-		int	ub = dimv[0] + lb[0] - 1;
-		indx = ub + 1;
+		lb = ARR_LBOUND(v);
+		dimv = ARR_DIMS(v);
+
+		if (arg0_elemid != InvalidOid)
+		{
+			/* append newelem */
+			int	ub = dimv[0] + lb[0] - 1;
+			indx = ub + 1;
+		}
+		else
+		{
+			/* prepend newelem */
+			indx = lb[0] - 1;
+		}
 	}
+	else if (ARR_NDIM(v) == 0)
+		indx = 1;
 	else
+		elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+	/*
+	 * We arrange to look up info about element type only once per series
+	 * of calls, assuming the element type doesn't change underneath us.
+	 */
+	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
 	{
-		/* prepend newelem */
-		indx = lb[0] - 1;
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+		my_extra->element_type = InvalidOid;
 	}
 
-	get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+	if (my_extra->element_type != element_type)
+	{
+		/* Get info about element type */
+		get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+
+		my_extra->element_type = element_type;
+		my_extra->typlen = typlen;
+		my_extra->typbyval = typbyval;
+		my_extra->typalign = typalign;
+	}
+	else
+	{
+		typlen = my_extra->typlen;
+		typbyval = my_extra->typbyval;
+		typalign = my_extra->typalign;
+	}
 
-	result = array_set(v, 1, &indx, newelem, -1,
-					   typlen, typbyval, typalign, &isNull);
+	result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
+												 typalign, &isNull);
 
 	PG_RETURN_ARRAYTYPE_P(result);
 }
@@ -145,13 +150,28 @@ array_cat(PG_FUNCTION_ARGS)
 
 	/*
 	 * We must have one of the following combinations of inputs:
-	 * 1) two arrays with ndims1 == ndims2
-	 * 2) ndims1 == ndims2 - 1
-	 * 3) ndims1 == ndims2 + 1
+	 * 1) one empty array, and one non-empty array
+	 * 2) both arrays empty
+	 * 3) two arrays with ndims1 == ndims2
+	 * 4) ndims1 == ndims2 - 1
+	 * 5) ndims1 == ndims2 + 1
 	 */
 	ndims1 = ARR_NDIM(v1);
 	ndims2 = ARR_NDIM(v2);
 
+	/*
+	 * short circuit - if one input array is empty, and the other is not,
+	 * we return the non-empty one as the result
+	 *
+	 * if both are empty, return the first one
+	 */
+	if (ndims1 == 0 && ndims2 > 0)
+		PG_RETURN_ARRAYTYPE_P(v2);
+
+	if (ndims2 == 0)
+		PG_RETURN_ARRAYTYPE_P(v1);
+
+	/* the rest fall into combo 2, 3, or 4 */
 	if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
 		elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
 					"%d dimensions", ndims1, ndims2);
@@ -266,147 +286,15 @@ array_cat(PG_FUNCTION_ARGS)
 	PG_RETURN_ARRAYTYPE_P(result);
 }
 
-/*----------------------------------------------------------------------------
- * array_accum :
- *		accumulator to build a 1-D array from input values -- this can be used
- *		to create custom aggregates.
- *
- * This function is not marked strict, so we have to be careful about nulls.
- *----------------------------------------------------------------------------
- */
-Datum
-array_accum(PG_FUNCTION_ARGS)
-{
-	/* return NULL if both arguments are NULL */
-	if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-		PG_RETURN_NULL();
-
-	/* create a new 1-D array from the new element if the array is NULL */
-	if (PG_ARGISNULL(0))
-	{
-		Oid			tgt_type = get_fn_expr_rettype(fcinfo);
-		Oid			tgt_elem_type;
-
-		if (tgt_type == InvalidOid)
-			elog(ERROR, "Cannot determine target array type");
-		tgt_elem_type = get_element_type(tgt_type);
-		if (tgt_elem_type == InvalidOid)
-			elog(ERROR, "Target type is not an array");
-
-		PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
-													 PG_GETARG_DATUM(1),
-													 1));
-	}
-
-	/* return the array if the new element is NULL */
-	if (PG_ARGISNULL(1))
-		PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
-	/*
-	 * Otherwise this is equivalent to array_push.  We hack the call a little
-	 * so that array_push can see the fn_expr information.
-	 */
-	return array_push(fcinfo);
-}
-
-/*-----------------------------------------------------------------------------
- * array_assign :
- *		assign an element of an array to a new value and return the
- *		redefined array
- *----------------------------------------------------------------------------
- */
-Datum
-array_assign(PG_FUNCTION_ARGS)
-{
-	ArrayType  *v;
-	int			idx_to_chg;
-	Datum		newelem;
-	int		   *dimv,
-			   *lb, ub;
-	ArrayType  *result;
-	bool		isNull;
-	Oid			element_type;
-	int16		typlen;
-	bool		typbyval;
-	char		typalign;
-
-	v = PG_GETARG_ARRAYTYPE_P(0);
-	idx_to_chg = PG_GETARG_INT32(1);
-	newelem = PG_GETARG_DATUM(2);
-
-	/* Sanity check: do we have a one-dimensional array */
-	if (ARR_NDIM(v) != 1)
-		elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-	lb = ARR_LBOUND(v);
-	dimv = ARR_DIMS(v);
-	ub = dimv[0] + lb[0] - 1;
-	if (idx_to_chg < lb[0] || idx_to_chg > ub)
-		elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
-
-	element_type = ARR_ELEMTYPE(v);
-	/* Sanity check: do we have a non-zero element type */
-	if (element_type == 0)
-		elog(ERROR, "Invalid array element type: %u", element_type);
-
-	get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-	result = array_set(v, 1, &idx_to_chg, newelem, -1,
-					   typlen, typbyval, typalign, &isNull);
-
-	PG_RETURN_ARRAYTYPE_P(result);
-}
-
-/*-----------------------------------------------------------------------------
- * array_subscript :
- *		return specific element of an array
- *----------------------------------------------------------------------------
- */
-Datum
-array_subscript(PG_FUNCTION_ARGS)
-{
-	ArrayType  *v;
-	int			idx;
-	int		   *dimv,
-			   *lb, ub;
-	Datum		result;
-	bool		isNull;
-	Oid			element_type;
-	int16		typlen;
-	bool		typbyval;
-	char		typalign;
-
-	v = PG_GETARG_ARRAYTYPE_P(0);
-	idx = PG_GETARG_INT32(1);
-
-	/* Sanity check: do we have a one-dimensional array */
-	if (ARR_NDIM(v) != 1)
-		elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-	lb = ARR_LBOUND(v);
-	dimv = ARR_DIMS(v);
-	ub = dimv[0] + lb[0] - 1;
-	if (idx < lb[0] || idx > ub)
-		elog(ERROR, "Cannot return nonexistent array element: %d", idx);
-
-	element_type = ARR_ELEMTYPE(v);
-	/* Sanity check: do we have a non-zero element type */
-	if (element_type == 0)
-		elog(ERROR, "Invalid array element type: %u", element_type);
-
-	get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-	result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
-
-	PG_RETURN_DATUM(result);
-}
 
 /*
- * actually does the work for singleton_array(), and array_accum() if it is
- * given a null input array.
+ * used by text_to_array() in varlena.c
  */
 ArrayType *
-create_singleton_array(Oid element_type, Datum element, int ndims)
+create_singleton_array(FunctionCallInfo fcinfo,
+					   Oid element_type,
+					   Datum element,
+					   int ndims)
 {
 	Datum	dvalues[1];
 	int16	typlen;
@@ -415,6 +303,7 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
 	int		dims[MAXDIM];
 	int		lbs[MAXDIM];
 	int		i;
+	ArrayMetaState *my_extra;
 
 	if (element_type == 0)
 		elog(ERROR, "Invalid array element type: %u", element_type);
@@ -429,7 +318,35 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
 		lbs[i] = 1;
 	}
 
-	get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+	/*
+	 * We arrange to look up info about element type only once per series
+	 * of calls, assuming the element type doesn't change underneath us.
+	 */
+	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+		my_extra->element_type = InvalidOid;
+	}
+
+	if (my_extra->element_type != element_type)
+	{
+		/* Get info about element type */
+		get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+
+		my_extra->element_type = element_type;
+		my_extra->typlen = typlen;
+		my_extra->typbyval = typbyval;
+		my_extra->typalign = typalign;
+	}
+	else
+	{
+		typlen = my_extra->typlen;
+		typbyval = my_extra->typbyval;
+		typalign = my_extra->typalign;
+	}
 
 	return construct_md_array(dvalues, ndims, dims, lbs, element_type,
 							  typlen, typbyval, typalign);
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index b53c896e4314b0fb9fd6f5378239e581e677ed54..27a805d9b28dd6e3af4456a4589113a3ddb75821 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.89 2003/05/09 23:01:45 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.90 2003/06/24 23:14:45 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,8 +21,10 @@
 #include "catalog/pg_type.h"
 #include "libpq/pqformat.h"
 #include "parser/parse_coerce.h"
+#include "parser/parse_oper.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/datum.h"
 #include "utils/memutils.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -70,16 +72,6 @@
 
 #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)
 
-/* I/O function selector for system_cache_lookup */
-typedef enum IOFuncSelector
-{
-	IOFunc_input,
-	IOFunc_output,
-	IOFunc_receive,
-	IOFunc_send
-} IOFuncSelector;
-
-
 static int	ArrayCount(char *str, int *dim, char typdelim);
 static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
 			 FmgrInfo *inputproc, Oid typelem, int32 typmod,
@@ -93,10 +85,6 @@ static Datum *ReadArrayBinary(StringInfo buf, int nitems,
 static void CopyArrayEls(char *p, Datum *values, int nitems,
 			 int typlen, bool typbyval, char typalign,
 			 bool freedata);
-static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-								int *typlen, bool *typbyval,
-								char *typdelim, Oid *typelem,
-								Oid *proc, char *typalign);
 static Datum ArrayCast(char *value, bool byval, int len);
 static int ArrayCastAndSet(Datum src,
 				int typlen, bool typbyval, char typalign,
@@ -119,7 +107,7 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
 				   char *destPtr,
 				   int *st, int *endp, char *srcPtr,
 				   int typlen, bool typbyval, char typalign);
-
+static int array_cmp(FunctionCallInfo fcinfo);
 
 /*---------------------------------------------------------------------
  * array_in :
@@ -154,12 +142,49 @@ array_in(PG_FUNCTION_ARGS)
 				dim[MAXDIM],
 				lBound[MAXDIM];
 	char		typalign;
+	ArrayMetaState *my_extra;
+
+	/*
+	 * We arrange to look up info about element type, including its input
+	 * conversion proc only once per series of calls, assuming the element
+	 * type doesn't change underneath us.
+	 */
+	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+		my_extra->element_type = InvalidOid;
+	}
 
-	/* Get info about element type, including its input conversion proc */
-	system_cache_lookup(element_type, IOFunc_input,
-						&typlen, &typbyval, &typdelim,
-						&typelem, &typinput, &typalign);
-	fmgr_info(typinput, &inputproc);
+	if (my_extra->element_type != element_type)
+	{
+		/* Get info about element type, including its input conversion proc */
+		get_type_metadata(element_type, IOFunc_input,
+							&typlen, &typbyval, &typdelim,
+							&typelem, &typinput, &typalign);
+		fmgr_info(typinput, &inputproc);
+
+		my_extra->element_type = element_type;
+		my_extra->typlen = typlen;
+		my_extra->typbyval = typbyval;
+		my_extra->typdelim = typdelim;
+		my_extra->typelem = typelem;
+		my_extra->typiofunc = typinput;
+		my_extra->typalign = typalign;
+		my_extra->proc = inputproc;
+	}
+	else
+	{
+		typlen = my_extra->typlen;
+		typbyval = my_extra->typbyval;
+		typdelim = my_extra->typdelim;
+		typelem = my_extra->typelem;
+		typinput = my_extra->typiofunc;
+		typalign = my_extra->typalign;
+		inputproc = my_extra->proc;
+	}
 
 	/* Make a modifiable copy of the input */
 	/* XXX why are we allocating an extra 2 bytes here? */
@@ -636,12 +661,51 @@ array_out(PG_FUNCTION_ARGS)
 				indx[MAXDIM];
 	int			ndim,
 			   *dim;
+	ArrayMetaState *my_extra;
 
 	element_type = ARR_ELEMTYPE(v);
-	system_cache_lookup(element_type, IOFunc_output,
-						&typlen, &typbyval, &typdelim,
-						&typelem, &typoutput, &typalign);
-	fmgr_info(typoutput, &outputproc);
+
+	/*
+	 * We arrange to look up info about element type, including its input
+	 * conversion proc only once per series of calls, assuming the element
+	 * type doesn't change underneath us.
+	 */
+	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+		my_extra->element_type = InvalidOid;
+	}
+
+	if (my_extra->element_type != element_type)
+	{
+		/* Get info about element type, including its output conversion proc */
+		get_type_metadata(element_type, IOFunc_output,
+							&typlen, &typbyval, &typdelim,
+							&typelem, &typoutput, &typalign);
+		fmgr_info(typoutput, &outputproc);
+
+		my_extra->element_type = element_type;
+		my_extra->typlen = typlen;
+		my_extra->typbyval = typbyval;
+		my_extra->typdelim = typdelim;
+		my_extra->typelem = typelem;
+		my_extra->typiofunc = typoutput;
+		my_extra->typalign = typalign;
+		my_extra->proc = outputproc;
+	}
+	else
+	{
+		typlen = my_extra->typlen;
+		typbyval = my_extra->typbyval;
+		typdelim = my_extra->typdelim;
+		typelem = my_extra->typelem;
+		typoutput = my_extra->typiofunc;
+		typalign = my_extra->typalign;
+		outputproc = my_extra->proc;
+	}
 
 	ndim = ARR_NDIM(v);
 	dim = ARR_DIMS(v);
@@ -800,6 +864,7 @@ array_recv(PG_FUNCTION_ARGS)
 				dim[MAXDIM],
 				lBound[MAXDIM];
 	char		typalign;
+	ArrayMetaState *my_extra;
 
 	/* Get the array header information */
 	ndim = pq_getmsgint(buf, 4);
@@ -831,14 +896,50 @@ array_recv(PG_FUNCTION_ARGS)
 		PG_RETURN_ARRAYTYPE_P(retval);
 	}
 
-	/* Get info about element type, including its receive conversion proc */
-	system_cache_lookup(element_type, IOFunc_receive,
-						&typlen, &typbyval, &typdelim,
-						&typelem, &typreceive, &typalign);
-	if (!OidIsValid(typreceive))
-		elog(ERROR, "No binary input function available for type %s",
-			 format_type_be(element_type));
-	fmgr_info(typreceive, &receiveproc);
+	/*
+	 * We arrange to look up info about element type, including its receive
+	 * conversion proc only once per series of calls, assuming the element
+	 * type doesn't change underneath us.
+	 */
+	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+		my_extra->element_type = InvalidOid;
+	}
+
+	if (my_extra->element_type != element_type)
+	{
+		/* Get info about element type, including its receive conversion proc */
+		get_type_metadata(element_type, IOFunc_receive,
+							&typlen, &typbyval, &typdelim,
+							&typelem, &typreceive, &typalign);
+		if (!OidIsValid(typreceive))
+			elog(ERROR, "No binary input function available for type %s",
+				 format_type_be(element_type));
+		fmgr_info(typreceive, &receiveproc);
+
+		my_extra->element_type = element_type;
+		my_extra->typlen = typlen;
+		my_extra->typbyval = typbyval;
+		my_extra->typdelim = typdelim;
+		my_extra->typelem = typelem;
+		my_extra->typiofunc = typreceive;
+		my_extra->typalign = typalign;
+		my_extra->proc = receiveproc;
+	}
+	else
+	{
+		typlen = my_extra->typlen;
+		typbyval = my_extra->typbyval;
+		typdelim = my_extra->typdelim;
+		typelem = my_extra->typelem;
+		typreceive = my_extra->typiofunc;
+		typalign = my_extra->typalign;
+		receiveproc = my_extra->proc;
+	}
 
 	dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
 							  typlen, typbyval, typalign,
@@ -976,15 +1077,54 @@ array_send(PG_FUNCTION_ARGS)
 	int			ndim,
 			   *dim;
 	StringInfoData buf;
+	ArrayMetaState *my_extra;
 
 	/* Get information about the element type and the array dimensions */
 	element_type = ARR_ELEMTYPE(v);
-	system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
-						&typdelim, &typelem, &typsend, &typalign);
-	if (!OidIsValid(typsend))
-		elog(ERROR, "No binary output function available for type %s",
-			 format_type_be(element_type));
-	fmgr_info(typsend, &sendproc);
+
+	/*
+	 * We arrange to look up info about element type, including its send
+	 * proc only once per series of calls, assuming the element
+	 * type doesn't change underneath us.
+	 */
+	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+		my_extra->element_type = InvalidOid;
+	}
+
+	if (my_extra->element_type != element_type)
+	{
+		/* Get info about element type, including its send proc */
+		get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval,
+							&typdelim, &typelem, &typsend, &typalign);
+		if (!OidIsValid(typsend))
+			elog(ERROR, "No binary output function available for type %s",
+				 format_type_be(element_type));
+		fmgr_info(typsend, &sendproc);
+
+		my_extra->element_type = element_type;
+		my_extra->typlen = typlen;
+		my_extra->typbyval = typbyval;
+		my_extra->typdelim = typdelim;
+		my_extra->typelem = typelem;
+		my_extra->typiofunc = typsend;
+		my_extra->typalign = typalign;
+		my_extra->proc = sendproc;
+	}
+	else
+	{
+		typlen = my_extra->typlen;
+		typbyval = my_extra->typbyval;
+		typdelim = my_extra->typdelim;
+		typelem = my_extra->typelem;
+		typsend = my_extra->typiofunc;
+		typalign = my_extra->typalign;
+		sendproc = my_extra->proc;
+	}
 
 	ndim = ARR_NDIM(v);
 	dim = ARR_DIMS(v);
@@ -1476,6 +1616,26 @@ array_set(ArrayType *array,
 	array = DatumGetArrayTypeP(PointerGetDatum(array));
 
 	ndim = ARR_NDIM(array);
+
+	/*
+	 * if number of dims is zero, i.e. an empty array, create an array
+	 * with nSubscripts dimensions, and set the lower bounds to the supplied
+	 * subscripts
+	 */
+	if (ndim == 0)
+	{
+		Oid		elmtype = ARR_ELEMTYPE(array);
+
+		for (i = 0; i < nSubscripts; i++)
+		{
+			dim[i] = 1;
+			lb[i] = indx[i];
+		}
+
+		return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+												elmlen, elmbyval, elmalign);
+	}
+
 	if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
 		elog(ERROR, "Invalid array subscripts");
 
@@ -1632,6 +1792,31 @@ array_set_slice(ArrayType *array,
 	/* note: we assume srcArray contains no toasted elements */
 
 	ndim = ARR_NDIM(array);
+
+	/*
+	 * if number of dims is zero, i.e. an empty array, create an array
+	 * with nSubscripts dimensions, and set the upper and lower bounds
+	 * to the supplied subscripts
+	 */
+	if (ndim == 0)
+	{
+		Datum  *dvalues;
+		int		nelems;
+		Oid		elmtype = ARR_ELEMTYPE(array);
+
+		deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+														&dvalues, &nelems);
+
+		for (i = 0; i < nSubscripts; i++)
+		{
+			dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+			lb[i] = lowerIndx[i];
+		}
+
+		return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+												 elmlen, elmbyval, elmalign);
+	}
+
 	if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
 		elog(ERROR, "Invalid array subscripts");
 
@@ -1811,6 +1996,13 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
 	Oid			typelem;
 	Oid			proc;
 	char	   *s;
+	typedef struct {
+		ArrayMetaState *inp_extra;
+		ArrayMetaState *ret_extra;
+	} am_extra;
+	am_extra  *my_extra;
+	ArrayMetaState *inp_extra;
+	ArrayMetaState *ret_extra;
 
 	/* Get input array */
 	if (fcinfo->nargs < 1)
@@ -1829,11 +2021,81 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
 	if (nitems <= 0)
 		PG_RETURN_ARRAYTYPE_P(v);
 
-	/* Lookup source and result types. Unneeded variables are reused. */
-	system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
-						&typdelim, &typelem, &proc, &inp_typalign);
-	system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
-						&typdelim, &typelem, &proc, &typalign);
+	/*
+	 * We arrange to look up info about input and return element types only
+	 * once per series of calls, assuming the element type doesn't change
+	 * underneath us.
+	 */
+	my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(am_extra));
+		my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
+
+		my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		inp_extra = my_extra->inp_extra;
+		inp_extra->element_type = InvalidOid;
+
+		my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		ret_extra = my_extra->ret_extra;
+		ret_extra->element_type = InvalidOid;
+	}
+	else
+	{
+		inp_extra = my_extra->inp_extra;
+		ret_extra = my_extra->ret_extra;
+	}
+
+	if (inp_extra->element_type != inpType)
+	{
+		/* Lookup source and result types. Unneeded variables are reused. */
+		get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
+							&typdelim, &typelem, &proc, &inp_typalign);
+
+		inp_extra->element_type = inpType;
+		inp_extra->typlen = inp_typlen;
+		inp_extra->typbyval = inp_typbyval;
+		inp_extra->typdelim = typdelim;
+		inp_extra->typelem = typelem;
+		inp_extra->typiofunc = proc;
+		inp_extra->typalign = inp_typalign;
+	}
+	else
+	{
+		inp_typlen = inp_extra->typlen;
+		inp_typbyval = inp_extra->typbyval;
+		typdelim = inp_extra->typdelim;
+		typelem = inp_extra->typelem;
+		proc = inp_extra->typiofunc;
+		inp_typalign = inp_extra->typalign;
+	}
+
+	if (ret_extra->element_type != retType)
+	{
+		/* Lookup source and result types. Unneeded variables are reused. */
+		get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
+							&typdelim, &typelem, &proc, &typalign);
+
+		ret_extra->element_type = retType;
+		ret_extra->typlen = typlen;
+		ret_extra->typbyval = typbyval;
+		ret_extra->typdelim = typdelim;
+		ret_extra->typelem = typelem;
+		ret_extra->typiofunc = proc;
+		ret_extra->typalign = typalign;
+	}
+	else
+	{
+		typlen = ret_extra->typlen;
+		typbyval = ret_extra->typbyval;
+		typdelim = ret_extra->typdelim;
+		typelem = ret_extra->typelem;
+		proc = ret_extra->typiofunc;
+		typalign = ret_extra->typalign;
+	}
 
 	/* Allocate temporary array for new values */
 	values = (Datum *) palloc(nitems * sizeof(Datum));
@@ -2049,8 +2311,6 @@ deconstruct_array(ArrayType *array,
  *		  compares two arrays for equality
  * result :
  *		  returns true if the arrays are equal, false otherwise.
- *
- * XXX bitwise equality is pretty bogus ...
  *-----------------------------------------------------------------------------
  */
 Datum
@@ -2058,12 +2318,118 @@ array_eq(PG_FUNCTION_ARGS)
 {
 	ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
 	ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+	char	   *p1 = (char *) ARR_DATA_PTR(array1);
+	char	   *p2 = (char *) ARR_DATA_PTR(array2);
+	int			ndims1 = ARR_NDIM(array1);
+	int			ndims2 = ARR_NDIM(array2);
+	int		   *dims1 = ARR_DIMS(array1);
+	int		   *dims2 = ARR_DIMS(array2);
+	int			nitems1 = ArrayGetNItems(ndims1, dims1);
+	int			nitems2 = ArrayGetNItems(ndims2, dims2);
+	Oid			element_type = ARR_ELEMTYPE(array1);
+	FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
 	bool		result = true;
+	int			typlen;
+	bool		typbyval;
+	char		typdelim;
+	Oid			typelem;
+	char		typalign;
+	Oid			typiofunc;
+	int			i;
+	ArrayMetaState *my_extra;
+	FunctionCallInfoData locfcinfo;
 
-	if (ARR_SIZE(array1) != ARR_SIZE(array2))
-		result = false;
-	else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
+	/* fast path if the arrays do not have the same number of elements */
+	if (nitems1 != nitems2)
 		result = false;
+	else
+	{
+		/*
+		 * We arrange to look up the equality function only once per series of
+		 * calls, assuming the element type doesn't change underneath us.
+		 */
+		my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+		if (my_extra == NULL)
+		{
+			ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
+													 sizeof(ArrayMetaState));
+			my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+			my_extra->element_type = InvalidOid;
+		}
+
+		if (my_extra->element_type != element_type)
+		{
+			Oid		opfuncid = equality_oper_funcid(element_type);
+
+			if (OidIsValid(opfuncid))
+				fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
+			else
+				elog(ERROR,
+					 "array_eq: cannot find equality operator for type: %u",
+					 element_type);
+
+			get_type_metadata(element_type, IOFunc_output,
+							  &typlen, &typbyval, &typdelim,
+							  &typelem, &typiofunc, &typalign);
+			
+			my_extra->element_type = element_type;
+			my_extra->typlen = typlen;
+			my_extra->typbyval = typbyval;
+			my_extra->typdelim = typdelim;
+			my_extra->typelem = typelem;
+			my_extra->typiofunc = typiofunc;
+			my_extra->typalign = typalign;
+		}
+		else
+		{
+			typlen = my_extra->typlen;
+			typbyval = my_extra->typbyval;
+			typdelim = my_extra->typdelim;
+			typelem = my_extra->typelem;
+			typiofunc = my_extra->typiofunc;
+			typalign = my_extra->typalign;
+		}
+
+		/*
+		 * apply the operator to each pair of array elements.
+		 */
+		MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+		locfcinfo.flinfo = &my_extra->proc;
+		locfcinfo.nargs = 2;
+
+		/* Loop over source data */
+		for (i = 0; i < nitems1; i++)
+		{
+			Datum	elt1;
+			Datum	elt2;
+			bool	oprresult;
+
+			/* Get element pair */
+			elt1 = fetch_att(p1, typbyval, typlen);
+			elt2 = fetch_att(p2, typbyval, typlen);
+
+			p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+			p1 = (char *) att_align(p1, typalign);
+
+			p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+			p2 = (char *) att_align(p2, typalign);
+
+			/*
+			 * Apply the operator to the element pair
+			 */
+			locfcinfo.arg[0] = elt1;
+			locfcinfo.arg[1] = elt2;
+			locfcinfo.argnull[0] = false;
+			locfcinfo.argnull[1] = false;
+			locfcinfo.isnull = false;
+			oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+			if (!oprresult)
+			{
+				result = false;
+				break;
+			}
+		}
+	}
 
 	/* Avoid leaking memory when handed toasted input. */
 	PG_FREE_IF_COPY(array1, 0);
@@ -2073,53 +2439,190 @@ array_eq(PG_FUNCTION_ARGS)
 }
 
 
-/***************************************************************************/
-/******************|		  Support  Routines			  |*****************/
-/***************************************************************************/
+/*-----------------------------------------------------------------------------
+ * array-array bool operators:
+ *		Given two arrays, iterate comparison operators
+ *		over the array. Uses logic similar to text comparison
+ *		functions, except element-by-element instead of
+ *		character-by-character.
+ *----------------------------------------------------------------------------
+ */
+Datum
+array_ne(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
+}
 
-static void
-system_cache_lookup(Oid element_type,
-					IOFuncSelector which_func,
-					int *typlen,
-					bool *typbyval,
-					char *typdelim,
-					Oid *typelem,
-					Oid *proc,
-					char *typalign)
+Datum
+array_lt(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
+}
+
+Datum
+array_gt(PG_FUNCTION_ARGS)
 {
-	HeapTuple	typeTuple;
-	Form_pg_type typeStruct;
-
-	typeTuple = SearchSysCache(TYPEOID,
-							   ObjectIdGetDatum(element_type),
-							   0, 0, 0);
-	if (!HeapTupleIsValid(typeTuple))
-		elog(ERROR, "cache lookup failed for type %u", element_type);
-	typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
-	*typlen = typeStruct->typlen;
-	*typbyval = typeStruct->typbyval;
-	*typdelim = typeStruct->typdelim;
-	*typelem = typeStruct->typelem;
-	*typalign = typeStruct->typalign;
-	switch (which_func)
-	{
-		case IOFunc_input:
-			*proc = typeStruct->typinput;
-			break;
-		case IOFunc_output:
-			*proc = typeStruct->typoutput;
-			break;
-		case IOFunc_receive:
-			*proc = typeStruct->typreceive;
-			break;
-		case IOFunc_send:
-			*proc = typeStruct->typsend;
-			break;
-	}
-	ReleaseSysCache(typeTuple);
+	PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
+}
+
+Datum
+array_le(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
+}
+
+Datum
+array_ge(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
+}
+
+Datum
+btarraycmp(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT32(array_cmp(fcinfo));
 }
 
+/*
+ * array_cmp()
+ * Internal comparison function for arrays.
+ *
+ * Returns -1, 0 or 1
+ */
+static int
+array_cmp(FunctionCallInfo fcinfo)
+{
+	ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
+	ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+	FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
+	Datum		opresult;
+	int			result = 0;
+	Oid			element_type = InvalidOid;
+	int			typlen;
+	bool		typbyval;
+	char		typdelim;
+	Oid			typelem;
+	char		typalign;
+	Oid			typiofunc;
+	Datum	   *dvalues1;
+	int			nelems1;
+	Datum	   *dvalues2;
+	int			nelems2;
+	int			min_nelems;
+	int			i;
+	typedef struct
+	{
+		Oid				element_type;
+		int				typlen;
+		bool			typbyval;
+		char			typdelim;
+		Oid				typelem;
+		Oid				typiofunc;
+		char			typalign;
+		FmgrInfo		eqproc;
+		FmgrInfo		ordproc;
+	} ac_extra;
+	ac_extra *my_extra;
+
+	element_type = ARR_ELEMTYPE(array1);
+
+	/*
+	 * We arrange to look up the element type operator function only once
+	 * per series of calls, assuming the element type and opname don't
+	 * change underneath us.
+	 */
+	my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
+	if (my_extra == NULL)
+	{
+		ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
+														 sizeof(ac_extra));
+		my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
+		my_extra->element_type = InvalidOid;
+	}
+
+	if (my_extra->element_type != element_type)
+	{
+		Oid		eqfuncid = equality_oper_funcid(element_type);
+		Oid		ordfuncid = ordering_oper_funcid(element_type);
+
+		fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
+		fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
+
+		if (my_extra->eqproc.fn_nargs != 2)
+			elog(ERROR, "Equality operator does not take 2 arguments: %u",
+																 eqfuncid);
+		if (my_extra->ordproc.fn_nargs != 2)
+			elog(ERROR, "Ordering operator does not take 2 arguments: %u",
+																 ordfuncid);
+
+		get_type_metadata(element_type, IOFunc_output,
+						  &typlen, &typbyval, &typdelim,
+						  &typelem, &typiofunc, &typalign);
+		
+		my_extra->element_type = element_type;
+		my_extra->typlen = typlen;
+		my_extra->typbyval = typbyval;
+		my_extra->typdelim = typdelim;
+		my_extra->typelem = typelem;
+		my_extra->typiofunc = InvalidOid;
+		my_extra->typalign = typalign;
+	}
+	else
+	{
+		typlen = my_extra->typlen;
+		typbyval = my_extra->typbyval;
+		typalign = my_extra->typalign;
+	}
+
+	/* extract a C array of arg array datums */
+	deconstruct_array(array1, element_type, typlen, typbyval, typalign,
+													&dvalues1, &nelems1);
+
+	deconstruct_array(array2, element_type, typlen, typbyval, typalign,
+													&dvalues2, &nelems2);
+
+	min_nelems = Min(nelems1, nelems2);
+	for (i = 0; i < min_nelems; i++)
+	{
+		/* are they equal */
+		opresult = FunctionCall2(&my_extra->eqproc,
+								 dvalues1[i], dvalues2[i]);
+
+		if (!DatumGetBool(opresult))
+		{
+			/* nope, see if arg1 is less than arg2 */
+			opresult = FunctionCall2(&my_extra->ordproc,
+									 dvalues1[i], dvalues2[i]);
+			if (DatumGetBool(opresult))
+			{
+				/* arg1 is less than arg2 */
+				result = -1;
+				break;
+			}
+			else
+			{
+				/* arg1 is greater than arg2 */
+				result = 1;
+				break;
+			}
+		}
+	}
+
+	if ((result == 0) && (nelems1 != nelems2))
+		result = (nelems1 < nelems2) ? -1 : 1;
+
+	/* Avoid leaking memory when handed toasted input. */
+	PG_FREE_IF_COPY(array1, 0);
+	PG_FREE_IF_COPY(array2, 1);
+
+	return result;
+}
+
+
+/***************************************************************************/
+/******************|		  Support  Routines			  |*****************/
+/***************************************************************************/
+
 /*
  * Fetch array element at pointer, converted correctly to a Datum
  */
@@ -2423,6 +2926,18 @@ array_type_coerce(PG_FUNCTION_ARGS)
 		if (tgt_elem_type == InvalidOid)
 			elog(ERROR, "Target type is not an array");
 
+		/*
+		 * We don't deal with domain constraints yet, so bail out.
+		 * This isn't currently a problem, because we also don't
+		 * support arrays of domain type elements either. But in the
+		 * future we might. At that point consideration should be given
+		 * to removing the check below and adding a domain constraints
+		 * check to the coercion.
+		 */
+		if (getBaseType(tgt_elem_type) != tgt_elem_type)
+			elog(ERROR, "array coercion to domain type elements not " \
+						"currently supported");
+
 		if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
 								   COERCION_EXPLICIT, &funcId))
 		{
@@ -2439,10 +2954,16 @@ array_type_coerce(PG_FUNCTION_ARGS)
 	}
 
 	/*
-	 * If it's binary-compatible, return the array unmodified.
+	 * If it's binary-compatible, modify the element type in the array header,
+	 * but otherwise leave the array as we received it.
 	 */
 	if (my_extra->coerce_finfo.fn_oid == InvalidOid)
-		PG_RETURN_ARRAYTYPE_P(src);
+	{
+		ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
+		
+		ARR_ELEMTYPE(result) = my_extra->desttype;
+		PG_RETURN_ARRAYTYPE_P(result);
+	}
 
 	/*
 	 * Use array_map to apply the function to each array element.
@@ -2454,3 +2975,118 @@ array_type_coerce(PG_FUNCTION_ARGS)
 
 	return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
 }
+
+/*
+ * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
+ *
+ *	astate is working state (NULL on first call)
+ *	rcontext is where to keep working state
+ */
+ArrayBuildState *
+accumArrayResult(ArrayBuildState *astate,
+				 Datum dvalue, bool disnull,
+				 Oid element_type,
+				 MemoryContext rcontext)
+{
+	MemoryContext arr_context,
+				  oldcontext;
+
+	if (astate == NULL)
+	{
+		/* First time through --- initialize */
+
+		/* Make a temporary context to hold all the junk */
+		arr_context = AllocSetContextCreate(rcontext,
+											"accumArrayResult",
+											ALLOCSET_DEFAULT_MINSIZE,
+											ALLOCSET_DEFAULT_INITSIZE,
+											ALLOCSET_DEFAULT_MAXSIZE);
+		oldcontext = MemoryContextSwitchTo(arr_context);
+		astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+		astate->mcontext = arr_context;
+		astate->dvalues = (Datum *)
+			palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+		astate->nelems = 0;
+		astate->element_type = element_type;
+		get_typlenbyvalalign(element_type,
+							 &astate->typlen,
+							 &astate->typbyval,
+							 &astate->typalign);
+	}
+	else
+	{
+		oldcontext = MemoryContextSwitchTo(astate->mcontext);
+		Assert(astate->element_type == element_type);
+		/* enlarge dvalues[] if needed */
+		if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+			astate->dvalues = (Datum *)
+				repalloc(astate->dvalues,
+						 (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+	}
+
+	if (disnull)
+		elog(ERROR, "NULL elements not allowed in Arrays");
+
+	/* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+	astate->dvalues[astate->nelems++] =
+		datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return astate;
+}
+
+/*
+ * makeArrayResult - produce final result of accumArrayResult
+ *
+ *	astate is working state (not NULL)
+ *	rcontext is where to construct result
+ */
+Datum
+makeArrayResult(ArrayBuildState *astate,
+				MemoryContext rcontext)
+{
+	int			dims[1];
+	int			lbs[1];
+
+	dims[0] = astate->nelems;
+	lbs[0] = 1;
+
+	return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+}
+
+/*
+ * makeMdArrayResult - produce md final result of accumArrayResult
+ *
+ *	astate is working state (not NULL)
+ *	rcontext is where to construct result
+ */
+Datum
+makeMdArrayResult(ArrayBuildState *astate,
+				int ndims,
+				int *dims,
+				int *lbs,
+				MemoryContext rcontext)
+{
+	ArrayType  *result;
+	MemoryContext oldcontext;
+
+	/* Build the final array result in rcontext */
+	oldcontext = MemoryContextSwitchTo(rcontext);
+
+	result = construct_md_array(astate->dvalues,
+								ndims,
+								dims,
+								lbs,
+								astate->element_type,
+								astate->typlen,
+								astate->typbyval,
+								astate->typalign);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Clean up all the junk */
+	MemoryContextDelete(astate->mcontext);
+
+	return PointerGetDatum(result);
+}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 6be21d241f1af84d810b6251a7000fbe144077e0..aca19d17e89feea74746848e413f2755b85187e4 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.98 2003/05/15 15:50:19 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.99 2003/06/24 23:14:46 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,11 +19,14 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "access/tuptoaster.h"
+#include "catalog/pg_type.h"
 #include "lib/stringinfo.h"
 #include "libpq/crypt.h"
 #include "libpq/pqformat.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/pg_locale.h"
+#include "utils/lsyscache.h"
 
 
 typedef struct varlena unknown;
@@ -1983,8 +1986,7 @@ split_text(PG_FUNCTION_ARGS)
 		if (fldnum == 1)		/* first field - just return the input
 								 * string */
 			PG_RETURN_TEXT_P(inputstring);
-		else
-/* otherwise return an empty string */
+		else					/* otherwise return an empty string */
 			PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
 	}
 
@@ -2004,8 +2006,7 @@ split_text(PG_FUNCTION_ARGS)
 		if (fldnum == 1)		/* first field - just return the input
 								 * string */
 			PG_RETURN_TEXT_P(inputstring);
-		else
-/* otherwise return an empty string */
+		else					/* otherwise return an empty string */
 			PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
 	}
 	else if ((start_posn != 0) && (end_posn == 0))
@@ -2028,6 +2029,191 @@ split_text(PG_FUNCTION_ARGS)
 	}
 }
 
+/*
+ * text_to_array
+ * parse input string
+ * return text array of elements
+ * based on provided field separator
+ */
+Datum
+text_to_array(PG_FUNCTION_ARGS)
+{
+	text	   *inputstring = PG_GETARG_TEXT_P(0);
+	int			inputstring_len = TEXTLEN(inputstring);
+	text	   *fldsep = PG_GETARG_TEXT_P(1);
+	int			fldsep_len = TEXTLEN(fldsep);
+	int			fldnum;
+	int			start_posn = 0;
+	int			end_posn = 0;
+	text	   *result_text = NULL;
+	ArrayBuildState *astate = NULL;
+	MemoryContext oldcontext = CurrentMemoryContext;
+
+	/* return NULL for empty input string */
+	if (inputstring_len < 1)
+		PG_RETURN_NULL();
+
+	/* empty field separator
+	 * return one element, 1D, array using the input string */
+	if (fldsep_len < 1)
+		PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+							  CStringGetDatum(inputstring), 1));
+
+	/* start with end position holding the initial start position */
+	end_posn = 0;
+	for (fldnum=1;;fldnum++)	/* field number is 1 based */
+	{
+		Datum	dvalue;
+		bool	disnull = false;
+
+		start_posn = end_posn;
+		end_posn = text_position(PointerGetDatum(inputstring),
+								 PointerGetDatum(fldsep),
+								 fldnum);
+
+		if ((start_posn == 0) && (end_posn == 0))	/* fldsep not found */
+		{
+			if (fldnum == 1)
+			{
+				/* first element
+				 * return one element, 1D, array using the input string */
+				PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+									  CStringGetDatum(inputstring), 1));
+			}
+			else
+			{
+				/* otherwise create array and exit */
+				PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+			}
+		}
+		else if ((start_posn != 0) && (end_posn == 0))
+		{
+			/* last field requested */
+			result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+		}
+		else if ((start_posn == 0) && (end_posn != 0))
+		{
+			/* first field requested */
+			result_text = LEFT(inputstring, fldsep);
+		}
+		else
+		{
+			/* prior to last field requested */
+			result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false);
+		}
+
+		/* stash away current value */
+		dvalue = PointerGetDatum(result_text);
+		astate = accumArrayResult(astate, dvalue,
+								  disnull, TEXTOID, oldcontext);
+
+	}
+
+	/* never reached -- keep compiler quiet */
+	PG_RETURN_NULL();
+}
+
+/*
+ * array_to_text
+ * concatenate Cstring representation of input array elements
+ * using provided field separator
+ */
+Datum
+array_to_text(PG_FUNCTION_ARGS)
+{
+	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+	char	   *fldsep = PG_TEXTARG_GET_STR(1);
+	int			nitems, *dims, ndims;
+	char	   *p;
+	Oid			element_type;
+	int			typlen;
+	bool		typbyval;
+	char		typdelim;
+	Oid			typoutput,
+				typelem;
+	FmgrInfo	outputproc;
+	char		typalign;
+	StringInfo	result_str = makeStringInfo();
+	int			i;
+	ArrayMetaState *my_extra;
+
+	p = ARR_DATA_PTR(v);
+	ndims = ARR_NDIM(v);
+	dims = ARR_DIMS(v);
+	nitems = ArrayGetNItems(ndims, dims);
+
+	/* if there are no elements, return an empty string */
+	if (nitems == 0)
+		PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+	element_type = ARR_ELEMTYPE(v);
+
+	/*
+	 * We arrange to look up info about element type, including its output
+	 * conversion proc only once per series of calls, assuming the element
+	 * type doesn't change underneath us.
+	 */
+	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL)
+	{
+		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+													 sizeof(ArrayMetaState));
+		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+		my_extra->element_type = InvalidOid;
+	}
+
+	if (my_extra->element_type != element_type)
+	{
+		/* Get info about element type, including its output conversion proc */
+		get_type_metadata(element_type, IOFunc_output,
+							&typlen, &typbyval, &typdelim,
+							&typelem, &typoutput, &typalign);
+		fmgr_info(typoutput, &outputproc);
+
+		my_extra->element_type = element_type;
+		my_extra->typlen = typlen;
+		my_extra->typbyval = typbyval;
+		my_extra->typdelim = typdelim;
+		my_extra->typelem = typelem;
+		my_extra->typiofunc = typoutput;
+		my_extra->typalign = typalign;
+		my_extra->proc = outputproc;
+	}
+	else
+	{
+		typlen = my_extra->typlen;
+		typbyval = my_extra->typbyval;
+		typdelim = my_extra->typdelim;
+		typelem = my_extra->typelem;
+		typoutput = my_extra->typiofunc;
+		typalign = my_extra->typalign;
+		outputproc = my_extra->proc;
+	}
+
+	for (i = 0; i < nitems; i++)
+	{
+		Datum		itemvalue;
+		char	   *value;
+
+		itemvalue = fetch_att(p, typbyval, typlen);
+
+		value = DatumGetCString(FunctionCall3(&outputproc,
+											  itemvalue,
+											  ObjectIdGetDatum(typelem),
+											  Int32GetDatum(-1)));
+
+		if (i > 0)
+			appendStringInfo(result_str, "%s%s", fldsep, value);
+		else
+			appendStringInfo(result_str, "%s", value);
+
+		p = att_addlength(p, typlen, PointerGetDatum(p));
+		p = (char *) att_align(p, typalign);
+	}
+
+	PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
+}
+
 #define HEXBASE 16
 /*
  * Convert a int32 to a string containing a base 16 (hex) representation of
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 095c5e6a8aad5732476f335ed858a4d4df50408f..a4fcd3fc286df6d9e9c7cb9fb2e2c42644e41341 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.96 2003/06/22 22:04:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.97 2003/06/24 23:14:46 momjian Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -718,6 +718,40 @@ get_func_rettype(Oid funcid)
 	return result;
 }
 
+/*
+ * get_func_argtypes
+ *		Given procedure id, return the function's argument types.
+ *		Also pass back the number of arguments.
+ */
+Oid *
+get_func_argtypes(Oid funcid, int *nargs)
+{
+	HeapTuple		tp;
+	Form_pg_proc	procstruct;
+	Oid			   *result = NULL;
+	int				i;
+
+	tp = SearchSysCache(PROCOID,
+						ObjectIdGetDatum(funcid),
+						0, 0, 0);
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "Function OID %u does not exist", funcid);
+
+	procstruct = (Form_pg_proc) GETSTRUCT(tp);
+	*nargs = (int) procstruct->pronargs;
+
+	if (*nargs > 0)
+	{
+		result = (Oid *) palloc(*nargs * sizeof(Oid));
+
+		for (i = 0; i < *nargs; i++)
+			result[i] = procstruct->proargtypes[i];
+	}
+
+	ReleaseSysCache(tp);
+	return result;
+}
+
 /*
  * get_func_retset
  *		Given procedure id, return the function's proretset flag.
@@ -1090,6 +1124,56 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
 	ReleaseSysCache(tp);
 }
 
+/*
+ * get_type_metadata
+ *
+ *		A six-fer:	given the type OID, return typlen, typbyval, typalign,
+ *					typdelim, typelem, IO function Oid. The IO function
+ *					returned is controlled by IOFuncSelector
+ */
+void
+get_type_metadata(Oid element_type,
+					IOFuncSelector which_func,
+					int *typlen,
+					bool *typbyval,
+					char *typdelim,
+					Oid *typelem,
+					Oid *proc,
+					char *typalign)
+{
+	HeapTuple	typeTuple;
+	Form_pg_type typeStruct;
+
+	typeTuple = SearchSysCache(TYPEOID,
+							   ObjectIdGetDatum(element_type),
+							   0, 0, 0);
+	if (!HeapTupleIsValid(typeTuple))
+		elog(ERROR, "cache lookup failed for type %u", element_type);
+	typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+	*typlen = typeStruct->typlen;
+	*typbyval = typeStruct->typbyval;
+	*typdelim = typeStruct->typdelim;
+	*typelem = typeStruct->typelem;
+	*typalign = typeStruct->typalign;
+	switch (which_func)
+	{
+		case IOFunc_input:
+			*proc = typeStruct->typinput;
+			break;
+		case IOFunc_output:
+			*proc = typeStruct->typoutput;
+			break;
+		case IOFunc_receive:
+			*proc = typeStruct->typreceive;
+			break;
+		case IOFunc_send:
+			*proc = typeStruct->typsend;
+			break;
+	}
+	ReleaseSysCache(typeTuple);
+}
+
 #ifdef NOT_USED
 char
 get_typalign(Oid typid)
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 2cd2e19946f156579c35a14bbba0dd510993dc26..d772a0a06cedf6aa2efa60c0c06c456061e53a6e 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.68 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.69 2003/06/24 23:14:46 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1673,3 +1673,29 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
 
 	return exprType((Node *) nth(argnum, args));
 }
+
+/*
+ * Get the OID of the function or operator
+ *
+ * Returns InvalidOid if information is not available
+ */
+Oid
+get_fn_expr_functype(FunctionCallInfo fcinfo)
+{
+	Node   *expr;
+
+	/*
+	 * can't return anything useful if we have no FmgrInfo or if
+	 * its fn_expr node has not been initialized
+	 */
+	if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
+		return InvalidOid;
+
+	expr = fcinfo->flinfo->fn_expr;
+	if (IsA(expr, FuncExpr))
+		return ((FuncExpr *) expr)->funcid;
+	else if (IsA(expr, OpExpr))
+		return ((OpExpr *) expr)->opno;
+	else
+		return InvalidOid;
+}
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index e2cb7560e175e678610421420d70cc12a95e57c5..43672f716f2d41ed4c78e9b9a310dfc69a9b8b46 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -16,7 +16,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amop.h,v 1.50 2003/06/22 22:04:55 tgl Exp $
+ * $Id: pg_amop.h,v 1.51 2003/06/24 23:14:46 momjian Exp $
  *
  * NOTES
  *	 the genbki.sh script reads this file and generates .bki
@@ -418,6 +418,15 @@ DATA(insert (	2098 3 f 2334 ));
 DATA(insert (	2098 4 f 2335 ));
 DATA(insert (	2098 5 f 2336 ));
 
+/*
+ *	btree array_ops
+ */
+
+DATA(insert (	 397 1 f 1072 ));
+DATA(insert (	 397 2 f 1074 ));
+DATA(insert (	 397 3 f 1070 ));
+DATA(insert (	 397 4 f 1075 ));
+DATA(insert (	 397 5 f 1073 ));
 
 /*
  *	hash index _ops
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index c2c37c136132ddc4d0f3b0d8556070a9cc3bd7df..87553a49807cd3a197c084149b692d491182f3b4 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -14,7 +14,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amproc.h,v 1.38 2003/06/22 22:04:55 tgl Exp $
+ * $Id: pg_amproc.h,v 1.39 2003/06/24 23:14:46 momjian Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -78,6 +78,7 @@ DATA(insert (	1993 3	199 ));
 
 
 /* btree */
+DATA(insert (	 397 1  382 ));
 DATA(insert (	 421 1	357 ));
 DATA(insert (	 423 1 1596 ));
 DATA(insert (	 424 1 1693 ));
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
index 1c8844b2fece39c1b08f5ae3a945c3d01a5844a3..828afe5aa8c4000a1efb3ff423ab76361174b1a8 100644
--- a/src/include/catalog/pg_opclass.h
+++ b/src/include/catalog/pg_opclass.h
@@ -26,7 +26,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_opclass.h,v 1.51 2003/06/22 22:04:55 tgl Exp $
+ * $Id: pg_opclass.h,v 1.52 2003/06/24 23:14:46 momjian Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -87,6 +87,8 @@ typedef FormData_pg_opclass *Form_pg_opclass;
  */
 
 DATA(insert OID =  421 (	403		abstime_ops		PGNSP PGUID  702 t 0 ));
+DATA(insert OID =  397 (	403		array_ops		PGNSP PGUID 2277 t 0 ));
+#define ARRAY_BTREE_OPS_OID 397
 DATA(insert OID =  422 (	402		bigbox_ops		PGNSP PGUID  603 f 0 ));
 DATA(insert OID =  423 (	403		bit_ops			PGNSP PGUID 1560 t 0 ));
 DATA(insert OID =  424 (	403		bool_ops		PGNSP PGUID   16 t 0 ));
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 88d3d998a05529cb14111f7afc8fb585b9328430..3a7b143c9f59eb8b174ea1918b4f77215f40a4d2 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.115 2003/06/22 22:04:55 tgl Exp $
+ * $Id: pg_operator.h,v 1.116 2003/06/24 23:14:46 momjian Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -116,10 +116,15 @@ DATA(insert OID =  96 ( "="		   PGNSP PGUID b t	23	23	16	96 518	97	97	97 521 int
 DATA(insert OID =  97 ( "<"		   PGNSP PGUID b f	23	23	16 521 525	 0	 0	 0	 0 int4lt scalarltsel scalarltjoinsel ));
 DATA(insert OID =  98 ( "="		   PGNSP PGUID b t	25	25	16	98 531 664 664 664 666 texteq eqsel eqjoinsel ));
 
-DATA(insert OID = 329 (  "="	   PGNSP PGUID b f 2277 2277 16   329 0 0 0	0 0 array_eq     eqsel eqjoinsel ));
-DATA(insert OID = 349 (  "||"	   PGNSP PGUID b f 2277 2283 2277   0 0 0 0	0 0 array_append   -       -     ));
-DATA(insert OID = 374 (  "||"	   PGNSP PGUID b f 2283 2277 2277   0 0 0 0	0 0 array_prepend  -       -     ));
-DATA(insert OID = 375 (  "||"	   PGNSP PGUID b f 2277 2277 2277   0 0 0 0	0 0 array_cat      -       -     ));
+DATA(insert OID = 1070 (  "="	   PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 1071 (  "<>"	   PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0 0 array_ne neqsel neqjoinsel ));
+DATA(insert OID = 1072 (  "<"	   PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1073 (  ">"	   PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1074 (  "<="	   PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1075 (  ">="	   PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel scalargtjoinsel ));
+DATA(insert OID = 349 (  "||"	   PGNSP PGUID b f 2277 2283 2277 0 0  0 0 0 0 array_append   -       -     ));
+DATA(insert OID = 374 (  "||"	   PGNSP PGUID b f 2283 2277 2277 0 0  0 0 0 0 array_prepend  -       -     ));
+DATA(insert OID = 375 (  "||"	   PGNSP PGUID b f 2277 2277 2277 0 0  0 0 0 0 array_cat      -       -     ));
 
 DATA(insert OID = 352 (  "="	   PGNSP PGUID b t	28	28	16 352	 0	 0	 0	 0	 0 xideq eqsel eqjoinsel ));
 DATA(insert OID = 353 (  "="	   PGNSP PGUID b f	28	23	16	 0	 0	 0	 0	 0	 0 xideqint4 eqsel eqjoinsel ));
@@ -425,6 +430,7 @@ DATA(insert OID = 965 (  "^"	   PGNSP PGUID b f	701  701	701 0 0 0 0 0 0 dpow -
 DATA(insert OID = 966 (  "+"	   PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
 DATA(insert OID = 967 (  "-"	   PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
 DATA(insert OID = 968 (  "~"	   PGNSP PGUID b f 1034 1033	 16 0 0 0 0 0 0 aclcontains - - ));
+DATA(insert OID = 974 (  "="	   PGNSP PGUID b f 1033 1033	 16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
 
 /* additional geometric operators - thomas 1997-07-09 */
 DATA(insert OID =  969 (  "@@"	   PGNSP PGUID l f	0  601	600    0  0 0 0 0 0 lseg_center - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 80d80b59397e664eb6713c754ae29daf83a59c0c..d4c2f64cb7e9cf01c4288cf5ac3ff66b5f289344 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.305 2003/06/24 22:21:23 momjian Exp $
+ * $Id: pg_proc.h,v 1.306 2003/06/24 23:14:47 momjian Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -758,6 +758,8 @@ DATA(insert OID = 359 (  btnamecmp		   PGNSP PGUID 12 f f t f i 2 23 "19 19"	btn
 DESCR("btree less-equal-greater");
 DATA(insert OID = 360 (  bttextcmp		   PGNSP PGUID 12 f f t f i 2 23 "25 25"	bttextcmp - _null_ ));
 DESCR("btree less-equal-greater");
+DATA(insert OID = 382 (  btarraycmp		   PGNSP PGUID 12 f f t f i 2 23 "2277 2277"	btarraycmp - _null_ ));
+DESCR("btree less-equal-greater");
 
 DATA(insert OID = 361 (  lseg_distance	   PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
 DESCR("distance between");
@@ -988,14 +990,23 @@ DESCR("greater-than");
 DATA(insert OID = 743 (  text_ge		   PGNSP PGUID 12 f f t f i 2 16 "25 25"	text_ge - _null_ ));
 DESCR("greater-than-or-equal");
 
-DATA(insert OID = 744 (  array_eq		   PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
-DESCR("array equal");
-
 DATA(insert OID = 745 (  current_user	   PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
 DESCR("current user name");
 DATA(insert OID = 746 (  session_user	   PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
 DESCR("session user name");
 
+DATA(insert OID = 744 (  array_eq		   PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
+DESCR("array equal");
+DATA(insert OID = 390 (  array_ne		   PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
+DESCR("array not equal");
+DATA(insert OID = 391 (  array_lt		   PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
+DESCR("array less than");
+DATA(insert OID = 392 (  array_gt		   PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
+DESCR("array greater than");
+DATA(insert OID = 393 (  array_le		   PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
+DESCR("array less than or equal");
+DATA(insert OID = 396 (  array_ge		   PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
+DESCR("array greater than or equal");
 DATA(insert OID = 747 (  array_dims		   PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
 DESCR("array dimensions");
 DATA(insert OID = 750 (  array_in		   PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
@@ -1006,22 +1017,18 @@ DATA(insert OID = 2091 (  array_lower	   PGNSP PGUID 12 f f t f i 2 23 "2277 23"
 DESCR("array lower dimension");
 DATA(insert OID = 2092 (  array_upper	   PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
 DESCR("array upper dimension");
-DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
-DESCR("create array from single element");
 DATA(insert OID = 378 (  array_append	   PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
 DESCR("append element onto end of array");
 DATA(insert OID = 379 (  array_prepend	   PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
 DESCR("prepend element onto front of array");
-DATA(insert OID = 380 (  array_accum	   PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
-DESCR("push element onto end of array, creating array if needed");
-DATA(insert OID = 381 (  array_assign	   PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
-DESCR("assign specific array element");
-DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
-DESCR("return specific array element");
 DATA(insert OID = 383 (  array_cat	       PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
 DESCR("concatenate two arrays");
 DATA(insert OID = 384  (  array_coerce	   PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
 DESCR("coerce array type to another array type");
+DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+DESCR("split delimited text into text[]");
+DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+DESCR("concatenate array elements, using delimiter, into text");
 
 DATA(insert OID = 760 (  smgrin			   PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
 DESCR("I/O");
@@ -1322,6 +1329,8 @@ DATA(insert OID = 1036 (  aclremove		   PGNSP PGUID 12 f f t f s 2 1034 "1034 10
 DESCR("remove ACL item");
 DATA(insert OID = 1037 (  aclcontains	   PGNSP PGUID 12 f f t f s 2 16 "1034 1033"	aclcontains - _null_ ));
 DESCR("does ACL contain item?");
+DATA(insert OID = 1062 (  aclitemeq	       PGNSP PGUID 12 f f t f s 2 16 "1033 1033"	aclitem_eq - _null_ ));
+DESCR("equality operator for ACL items");
 DATA(insert OID = 1365 (  makeaclitem	   PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16"	makeaclitem - _null_ ));
 DESCR("make ACL item");
 DATA(insert OID = 1038 (  seteval		   PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 620f06bd18c4bd3d134edb027b89a649c297aeca..784b50885ea257cfece5f6d5db74e5a6a4f862bd 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -11,13 +11,14 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fmgr.h,v 1.27 2003/04/08 23:20:04 tgl Exp $
+ * $Id: fmgr.h,v 1.28 2003/06/24 23:14:46 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef FMGR_H
 #define FMGR_H
 
+#include "nodes/nodes.h"
 
 /*
  * All functions that can be called directly by fmgr must have this signature.
@@ -372,14 +373,14 @@ extern Datum OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2,
 				 Datum arg6, Datum arg7, Datum arg8,
 				 Datum arg9);
 
-
 /*
  * Routines in fmgr.c
  */
 extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
-extern Oid	fmgr_internal_function(const char *proname);
-extern Oid	get_fn_expr_rettype(FunctionCallInfo fcinfo);
-extern Oid	get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
+extern Oid fmgr_internal_function(const char *proname);
+extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
+extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
+extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);
 
 /*
  * Routines in dfmgr.c
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 12af0fd0925049fa432da2e151019d08ab4b9ff8..3756e443ff7e3e836f956178be372c961e31881a 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.83 2003/06/06 15:04:03 tgl Exp $
+ * $Id: primnodes.h,v 1.84 2003/06/24 23:14:48 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -226,6 +226,7 @@ typedef struct Aggref
 	Index		agglevelsup;	/* > 0 if agg belongs to outer query */
 	bool		aggstar;		/* TRUE if argument was really '*' */
 	bool		aggdistinct;	/* TRUE if it's agg(DISTINCT ...) */
+	List	   *args;			/* arguments to the aggregate */
 } Aggref;
 
 /* ----------------
@@ -358,15 +359,19 @@ typedef struct BoolExpr
 /* ----------------
  * SubLink
  *
- * A SubLink represents a subselect appearing in an expression, and in some
- * cases also the combining operator(s) just above it.	The subLinkType
- * indicates the form of the expression represented:
+ * A SubLink represents a subselect, or an expression, appearing in an
+ * expression, and in some cases also the combining operator(s) just above
+ * it.	The subLinkType indicates the form of the expression represented:
  *	EXISTS_SUBLINK		EXISTS(SELECT ...)
  *	ALL_SUBLINK			(lefthand) op ALL (SELECT ...)
  *	ANY_SUBLINK			(lefthand) op ANY (SELECT ...)
  *	MULTIEXPR_SUBLINK	(lefthand) op (SELECT ...)
  *	EXPR_SUBLINK		(SELECT with single targetlist item ...)
  *	ARRAY_SUBLINK		ARRAY(SELECT with single targetlist item ...)
+ * If an expression is used in place of the subselect, it is transformed
+ * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
+ * used as if they were the result of a single column subselect. If the
+ * expression is scalar, it is treated as a one element array.
  * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
  * same length as the subselect's targetlist.  MULTIEXPR will *always* have
  * a list with more than one entry; if the subselect has just one target
@@ -415,6 +420,8 @@ typedef struct SubLink
 	SubLinkType subLinkType;	/* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
 	bool		useOr;			/* TRUE to combine column results with
 								 * "OR" not "AND" */
+	bool		isExpr;			/* TRUE if the subselect is really derived
+								 * from a single expression */
 	List	   *lefthand;		/* list of outer-query expressions on the
 								 * left */
 	List	   *operName;		/* originally specified operator name */
@@ -456,6 +463,15 @@ typedef struct SubPlan
 	SubLinkType subLinkType;	/* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
 	bool		useOr;			/* TRUE to combine column results with
 								 * "OR" not "AND" */
+	bool		isExpr;			/* TRUE if the subselect is really derived
+								 * from a single expression */
+	/* runtime cache for single array expressions */
+	Oid			exprtype;		/* array and element type, and other info
+								 * needed deconstruct the array */
+	Oid			elemtype;
+	int16		elmlen;
+	bool		elmbyval;
+	char		elmalign;
 	/* The combining operators, transformed to executable expressions: */
 	List	   *exprs;			/* list of OpExpr expression trees */
 	List	   *paramIds;		/* IDs of Params embedded in the above */
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 77dd37659515fbd4ec75e5465aabd1df15df233e..946fc68c463e0fafa0e6f31c82bb677db474e86e 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.63 2003/05/28 16:04:02 tgl Exp $
+ * $Id: clauses.h,v 1.64 2003/06/24 23:14:49 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,9 @@ extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
 extern Node *get_leftop(Expr *clause);
 extern Node *get_rightop(Expr *clause);
 
+extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
+									CoercionForm funcformat, List *funcargs);
+
 extern bool not_clause(Node *clause);
 extern Expr *make_notclause(Expr *notclause);
 extern Expr *get_notclausearg(Expr *notclause);
diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h
index 6d2268bf5573529840c8a13d77f5009841039343..e7c8bdb56cbeec47a9818dbeb727dfda4ec1d27c 100644
--- a/src/include/parser/parse_oper.h
+++ b/src/include/parser/parse_oper.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_oper.h,v 1.25 2003/04/29 22:13:11 tgl Exp $
+ * $Id: parse_oper.h,v 1.26 2003/06/24 23:14:49 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,7 @@ extern Operator ordering_oper(Oid argtype, bool noError);
 /* Convenience routines for common calls on the above */
 extern Oid	compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
 extern Oid	equality_oper_funcid(Oid argtype);
+extern Oid  ordering_oper_funcid(Oid argtype);
 extern Oid	ordering_oper_opid(Oid argtype);
 
 /* Extract operator OID or underlying-function OID from an Operator tuple */
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 1a54042e5e9f6913fbbfe0b1c3022c0d84ffd2b3..0d7761190196cf15d728a1349af40b55c2182ea2 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: acl.h,v 1.52 2003/06/11 09:23:55 petere Exp $
+ * $Id: acl.h,v 1.53 2003/06/24 23:14:49 momjian Exp $
  *
  * NOTES
  *	  For backward-compatibility purposes we have to allow there
@@ -192,6 +192,7 @@ extern Datum aclinsert(PG_FUNCTION_ARGS);
 extern Datum aclremove(PG_FUNCTION_ARGS);
 extern Datum aclcontains(PG_FUNCTION_ARGS);
 extern Datum makeaclitem(PG_FUNCTION_ARGS);
+extern Datum aclitem_eq(PG_FUNCTION_ARGS);
 
 /*
  * prototypes for functions in aclchk.c
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 23a1d2b30e01e32f134bc0d77090db6de2a0e9eb..5df86b1827f3bdc4eafbb509eddb78e08a9dc206 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: array.h,v 1.38 2003/05/08 22:19:57 tgl Exp $
+ * $Id: array.h,v 1.39 2003/06/24 23:14:49 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,37 @@ typedef struct
 	Oid			elemtype;		/* element type OID */
 } ArrayType;
 
+typedef struct ArrayBuildState
+{
+	MemoryContext mcontext;		/* where all the temp stuff is kept */
+	Datum	   *dvalues;		/* array of accumulated Datums */
+	/*
+	 * The allocated size of dvalues[] is always a multiple of
+	 * ARRAY_ELEMS_CHUNKSIZE
+	 */
+#define ARRAY_ELEMS_CHUNKSIZE	64
+	int			nelems;			/* number of valid Datums in dvalues[] */
+	Oid			element_type;	/* data type of the Datums */
+	int16		typlen;			/* needed info about datatype */
+	bool		typbyval;
+	char		typalign;
+} ArrayBuildState;
+
+/*
+ * structure to cache type metadata needed for array manipulation
+ */
+typedef struct ArrayMetaState
+{
+	Oid				element_type;
+	int				typlen;
+	bool			typbyval;
+	char			typdelim;
+	Oid				typelem;
+	Oid				typiofunc;
+	char			typalign;
+	FmgrInfo		proc;
+} ArrayMetaState;
+
 /*
  * fmgr macros for array objects
  */
@@ -86,11 +117,15 @@ extern Datum array_recv(PG_FUNCTION_ARGS);
 extern Datum array_send(PG_FUNCTION_ARGS);
 extern Datum array_length_coerce(PG_FUNCTION_ARGS);
 extern Datum array_eq(PG_FUNCTION_ARGS);
+extern Datum array_ne(PG_FUNCTION_ARGS);
+extern Datum array_lt(PG_FUNCTION_ARGS);
+extern Datum array_gt(PG_FUNCTION_ARGS);
+extern Datum array_le(PG_FUNCTION_ARGS);
+extern Datum array_ge(PG_FUNCTION_ARGS);
+extern Datum btarraycmp(PG_FUNCTION_ARGS);
 extern Datum array_dims(PG_FUNCTION_ARGS);
 extern Datum array_lower(PG_FUNCTION_ARGS);
 extern Datum array_upper(PG_FUNCTION_ARGS);
-extern Datum array_assign(PG_FUNCTION_ARGS);
-extern Datum array_subscript(PG_FUNCTION_ARGS);
 extern Datum array_type_coerce(PG_FUNCTION_ARGS);
 
 extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
@@ -124,7 +159,14 @@ extern void deconstruct_array(ArrayType *array,
 				  Oid elmtype,
 				  int elmlen, bool elmbyval, char elmalign,
 				  Datum **elemsp, int *nelemsp);
-
+extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
+										 Datum dvalue, bool disnull,
+										 Oid element_type,
+										 MemoryContext rcontext);
+extern Datum makeArrayResult(ArrayBuildState *astate,
+							 MemoryContext rcontext);
+extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
+							   int *dims, int *lbs, MemoryContext rcontext);
 
 /*
  * prototypes for functions defined in arrayutils.c
@@ -141,12 +183,11 @@ extern int	mda_next_tuple(int n, int *curr, int *span);
 /*
  * prototypes for functions defined in array_userfuncs.c
  */
-extern Datum singleton_array(PG_FUNCTION_ARGS);
 extern Datum array_push(PG_FUNCTION_ARGS);
-extern Datum array_accum(PG_FUNCTION_ARGS);
 extern Datum array_cat(PG_FUNCTION_ARGS);
 
-extern ArrayType *create_singleton_array(Oid element_type,
+extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
+										 Oid element_type,
 										 Datum element,
 										 int ndims);
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 099b1ffa3786aca4cfcd8537ec894289f6ae1754..43e174722b3e6ccca4fc2bc44aae3ffeb95fc367 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.220 2003/06/24 22:21:23 momjian Exp $
+ * $Id: builtins.h,v 1.221 2003/06/24 23:14:49 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -530,6 +530,8 @@ extern bool SplitIdentifierString(char *rawstring, char separator,
 					  List **namelist);
 extern Datum replace_text(PG_FUNCTION_ARGS);
 extern Datum split_text(PG_FUNCTION_ARGS);
+extern Datum text_to_array(PG_FUNCTION_ARGS);
+extern Datum array_to_text(PG_FUNCTION_ARGS);
 extern Datum to_hex32(PG_FUNCTION_ARGS);
 extern Datum to_hex64(PG_FUNCTION_ARGS);
 extern Datum md5_text(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 878f5445c2a768506afccdc4ac53c8a2c41970a3..0b1badd6efe964b606e2b2b336b3b7e0a76f210f 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.71 2003/06/22 22:04:55 tgl Exp $
+ * $Id: lsyscache.h,v 1.72 2003/06/24 23:14:49 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,15 @@
 
 #include "access/htup.h"
 
+/* I/O function selector for system_cache_lookup */
+typedef enum IOFuncSelector
+{
+	IOFunc_input,
+	IOFunc_output,
+	IOFunc_receive,
+	IOFunc_send
+} IOFuncSelector;
+
 extern bool op_in_opclass(Oid opno, Oid opclass);
 extern bool op_requires_recheck(Oid opno, Oid opclass);
 extern Oid	get_opclass_member(Oid opclass, int16 strategy);
@@ -41,6 +50,7 @@ extern RegProcedure get_oprrest(Oid opno);
 extern RegProcedure get_oprjoin(Oid opno);
 extern char *get_func_name(Oid funcid);
 extern Oid	get_func_rettype(Oid funcid);
+extern Oid *get_func_argtypes(Oid funcid, int *nargs);
 extern bool get_func_retset(Oid funcid);
 extern bool func_strict(Oid funcid);
 extern char func_volatile(Oid funcid);
@@ -56,6 +66,14 @@ extern bool get_typbyval(Oid typid);
 extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
 extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
 					 char *typalign);
+extern void get_type_metadata(Oid element_type,
+								IOFuncSelector which_func,
+								int *typlen,
+								bool *typbyval,
+								char *typdelim,
+								Oid *typelem,
+								Oid *proc,
+								char *typalign);
 extern char get_typstorage(Oid typid);
 extern int32 get_typtypmod(Oid typid);
 extern Node *get_typdefault(Oid typid);
diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y
index 7a8c2284bd26bc4e6612684e1389d753a2b9415f..92776168822ec05eecb510229f97365418bc648f 100644
--- a/src/interfaces/ecpg/preproc/preproc.y
+++ b/src/interfaces/ecpg/preproc/preproc.y
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.236 2003/06/20 13:36:34 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.237 2003/06/24 23:14:49 momjian Exp $ */
 
 /* Copyright comment */
 %{
@@ -4595,7 +4595,7 @@ type_declaration: S_TYPEDEF
 			    $3.type_enum != ECPGt_char &&
 	        	    $3.type_enum != ECPGt_unsigned_char &&
 			    atoi(this->type->type_index) >= 0)
-				mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+				mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
 
 			types = this;
 		}
@@ -5415,7 +5415,7 @@ ECPGTypedef: TYPE_P
 					$5.type_enum != ECPGt_char &&
 					$5.type_enum != ECPGt_unsigned_char &&
 					atoi(this->type->type_index) >= 0)
-					mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+					mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
 
 				types = this;
 			}
@@ -5482,7 +5482,7 @@ ECPGVar: SQL_VAR
 
 					default:
 						if (atoi(length) >= 0)
-							mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+							mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
 
 						if (atoi(dimension) < 0)
 							type = ECPGmake_simple_type($5.type_enum, make_str("1"));
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index 5f2dd86bb55f15fedccc6e005aa36cd255b2118a..80406bbccc5124115b194c0d5c71098e104dcb05 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -504,7 +504,7 @@ ECPGfree_type(struct ECPGtype * type)
 				switch (type->u.element->type)
 				{
 					case ECPGt_array:
-						yyerror("internal error, found multi-dimensional array\n");
+						yyerror("internal error, found multidimensional array\n");
 						break;
 					case ECPGt_struct:
 					case ECPGt_union:
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index be96e18c0c5488d38bc3fe5ba0eafb644d39436c..9fa2ec8a6c9a3756975e9f8d93fb1c6cdcc295a6 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -436,7 +436,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 	if (atoi(type_index) >= 0)
 	{
 		if (atoi(*length) >= 0)
-			mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+			mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
 
 		*length = type_index;
 	}
@@ -444,7 +444,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 	if (atoi(type_dimension) >= 0)
 	{
 		if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
-			mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+			mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
 
 		if (atoi(*dimension) >= 0)
 			*length = *dimension;
@@ -463,10 +463,10 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 		mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
 
 	if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
-		mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+		mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
 
 	if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
-		mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+		mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
 
 	switch (type_enum)
 	{
@@ -480,7 +480,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 			}
 
 			if (atoi(*length) >= 0)
-				mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
+				mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
 
 			break;
 		case ECPGt_varchar:
@@ -525,7 +525,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 			}
 
 			if (atoi(*length) >= 0)
-				mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
+				mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
 
 			break;
 	}
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 617cf09a9a00a1f6b36339e019a60a7c3c317443..c5f638bd4653e23c9e98ac7cbb0d4614e7c3cfa6 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -178,19 +178,13 @@ SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
 (1 row)
 
 -- functions
-SELECT singleton_array(42) AS "{42}";
- {42} 
-------
- {42}
-(1 row)
-
-SELECT array_append(singleton_array(42), 6) AS "{42,6}";
+SELECT array_append(array[42], 6) AS "{42,6}";
  {42,6} 
 --------
  {42,6}
 (1 row)
 
-SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
+SELECT array_prepend(6, array[42]) AS "{6,42}";
  {6,42} 
 --------
  {6,42}
@@ -214,24 +208,6 @@ SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
  {{3,4},{5,6},{1,2}}
 (1 row)
 
-SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
- 1.2 
------
- 1.2
-(1 row)
-
-SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
- {1.1,9.99,1.3} 
-----------------
- {1.1,9.99,1.3}
-(1 row)
-
-SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
- 9.99 
-------
- 9.99
-(1 row)
-
 -- operators
 SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
        a       
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 82eff24125df262f27466880cf96b500991134d1..e39044e40acabe1ba64f02c0be6b0ffd4219b5fc 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -130,15 +130,11 @@ SELECT ARRAY[ARRAY['hello'],ARRAY['world']];
 SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
 
 -- functions
-SELECT singleton_array(42) AS "{42}";
-SELECT array_append(singleton_array(42), 6) AS "{42,6}";
-SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
+SELECT array_append(array[42], 6) AS "{42,6}";
+SELECT array_prepend(6, array[42]) AS "{6,42}";
 SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
 SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
 SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
-SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
-SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
-SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
 
 -- operators
 SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];