Re: array support patch phase 1 patch - Mailing list pgsql-patches

From Joe Conway
Subject Re: array support patch phase 1 patch
Date
Msg-id 3EF7BF26.5050702@joeconway.com
Whole thread Raw
In response to Re: array support patch phase 1 patch  (Bruce Momjian <pgman@candle.pha.pa.us>)
Responses Re: array support patch phase 1 patch  (Bruce Momjian <pgman@candle.pha.pa.us>)
Re: array support patch phase 1 patch  (Bruce Momjian <pgman@candle.pha.pa.us>)
List pgsql-patches
Bruce Momjian wrote:
> Your patch has been added to the PostgreSQL unapplied patches list at:
[...snip...]
>>Joe Conway wrote:
>>>The attached patch addresses Peter's concerns, subject to our agreements
>>>above. I.e, the changes are:
>>
>>The previous patch was no longer applying cleanly, so here is an update.
>>Applies and compiles clean on cvs tip, passes all regression tests.
>>Please apply.

Here's another update against cvs tip. The previous version now has a
duplicate oid conflict -- fixed.

Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    24 Jun 2003 02:24:06 -0000
***************
*** 60,73 ****
  </programlisting>
   </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.
    </para>
   </note>
   </sect2>

   <sect2>
--- 60,133 ----
  </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>
+    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,142 ****
  </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
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   is specified.
   </para>

   <para>
--- 192,221 ----
  </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
    <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
    A lower bound of 1 is assumed for any subscript where only one value
!   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,153 ****
      WHERE name = 'Carol';
  </programlisting>

!   or updated at a single element:

  <programlisting>
  UPDATE sal_emp SET pay_by_quarter[4] = 15000
--- 226,248 ----
      WHERE name = 'Carol';
  </programlisting>

!   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,165 ****
--- 255,268 ----
  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>
***************
*** 179,184 ****
--- 282,369 ----
   </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:

***************
*** 194,199 ****
--- 379,394 ----
   </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
    all considered to be of the same type, regardless of size or number
***************
*** 300,305 ****
--- 495,566 ----
     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>
***************
*** 316,321 ****
--- 577,590 ----
     Alternatively, you can use backslash-escaping to protect all data characters
     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
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    24 Jun 2003 02:24:06 -0000
***************
*** 6962,6967 ****
--- 6962,7164 ----

    </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>
Index: src/backend/catalog/pg_aggregate.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
retrieving revision 1.56
diff -c -r1.56 pg_aggregate.c
*** src/backend/catalog/pg_aggregate.c    18 Sep 2002 21:35:20 -0000    1.56
--- src/backend/catalog/pg_aggregate.c    24 Jun 2003 02:24:06 -0000
***************
*** 50,59 ****
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     int            nargs;
      Oid            procOid;
      TupleDesc    tupDesc;
      int            i;
      ObjectAddress myself,
                  referenced;

--- 50,65 ----
      Oid            finalfn = InvalidOid;    /* can be omitted */
      Oid            finaltype;
      Oid            fnArgs[FUNC_MAX_ARGS];
!     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,91 ****
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs = 2;
      }
!     transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
      if (!OidIsValid(transfn))
!         func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
      tup = SearchSysCache(PROCOID,
                           ObjectIdGetDatum(transfn),
                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
!         func_error("AggregateCreate", aggtransfnName, nargs, 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
--- 74,122 ----
      MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
      fnArgs[0] = aggTransType;
      if (aggBaseType == ANYOID)
!         nargs_transfn = 1;
      else
      {
          fnArgs[1] = aggBaseType;
!         nargs_transfn = 2;
      }
!
!     /*
!      * 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_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_transfn, fnArgs, NULL);
      proc = (Form_pg_proc) GETSTRUCT(tup);

      /*
       * If the transfn is strict and the initval is NULL, make sure input
***************
*** 105,121 ****
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         tup = SearchSysCache(PROCOID,
!                              ObjectIdGetDatum(finalfn),
!                              0, 0, 0);
!         if (!HeapTupleIsValid(tup))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!         proc = (Form_pg_proc) GETSTRUCT(tup);
!         finaltype = proc->prorettype;
!         ReleaseSysCache(tup);
      }
      else
      {
--- 136,161 ----
      {
          MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
          fnArgs[0] = aggTransType;
!         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);
!
!         if (!OidIsValid(finalfn))
              func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
!
!         /*
!          * 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
      {
***************
*** 125,130 ****
--- 165,191 ----
          finaltype = aggTransType;
      }
      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
Index: src/backend/commands/aggregatecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
retrieving revision 1.5
diff -c -r1.5 aggregatecmds.c
*** src/backend/commands/aggregatecmds.c    4 Sep 2002 20:31:14 -0000    1.5
--- src/backend/commands/aggregatecmds.c    24 Jun 2003 02:24:06 -0000
***************
*** 119,125 ****
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p')
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

--- 119,127 ----
          baseTypeId = typenameTypeId(baseType);

      transTypeId = typenameTypeId(transType);
!     if (get_typtype(transTypeId) == 'p' &&
!         transTypeId != ANYARRAYOID &&
!         transTypeId != ANYELEMENTOID)
          elog(ERROR, "Aggregate transition datatype cannot be %s",
               format_type_be(transTypeId));

Index: src/backend/executor/execQual.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
retrieving revision 1.130
diff -c -r1.130 execQual.c
*** src/backend/executor/execQual.c    28 May 2003 22:32:49 -0000    1.130
--- src/backend/executor/execQual.c    24 Jun 2003 02:24:06 -0000
***************
*** 1528,1544 ****
              {
                  /* Check other sub-arrays are compatible */
                  if (elem_ndims != ARR_NDIM(array))
!                     elog(ERROR, "Multiple dimension 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 "
                           "expressions with matching dimensions");

                  if (memcmp(elem_lbs, ARR_LBOUND(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multiple dimension arrays must have array "
                           "expressions with matching dimensions");
              }

--- 1528,1544 ----
              {
                  /* Check other sub-arrays are compatible */
                  if (elem_ndims != ARR_NDIM(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, "Multidimensional arrays must have array "
                           "expressions with matching dimensions");

                  if (memcmp(elem_lbs, ARR_LBOUND(array),
                             elem_ndims * sizeof(int)) != 0)
!                     elog(ERROR, "Multidimensional arrays must have array "
                           "expressions with matching dimensions");
              }

Index: src/backend/executor/nodeAgg.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
retrieving revision 1.107
diff -c -r1.107 nodeAgg.c
*** src/backend/executor/nodeAgg.c    22 Jun 2003 22:04:54 -0000    1.107
--- src/backend/executor/nodeAgg.c    24 Jun 2003 02:24:06 -0000
***************
*** 58,63 ****
--- 58,64 ----
  #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,218 ****
  static void agg_fill_hash_table(AggState *aggstate);
  static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
  static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
!

  /*
   * Initialize all aggregates for a new group of input values.
--- 213,219 ----
  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,364 ****
      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);

      /*
--- 352,363 ----
***************
*** 1187,1193 ****
--- 1186,1206 ----
          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,1243 ****
--- 1251,1416 ----
                          &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,1263 ****
              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);

          /*
           * If the transfn is strict and the initval is NULL, make sure
--- 1423,1429 ----
              peraggstate->initValue = (Datum) 0;
          else
              peraggstate->initValue = GetAggInitVal(textInitVal,
!                                                    transfn_arg_types[0]);

          /*
           * If the transfn is strict and the initval is NULL, make sure
***************
*** 1468,1471 ****
--- 1634,1670 ----
      elog(ERROR, "Aggregate function %u called as normal function",
           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;
  }
Index: src/backend/executor/nodeSubplan.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
retrieving revision 1.47
diff -c -r1.47 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    22 Jun 2003 22:04:54 -0000    1.47
--- src/backend/executor/nodeSubplan.c    24 Jun 2003 02:24:06 -0000
***************
*** 28,50 ****
  #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);
--- 28,33 ----
***************
*** 54,66 ****
  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
--- 37,42 ----
***************
*** 224,229 ****
--- 200,206 ----
      PlanState  *planstate = node->planstate;
      SubLinkType subLinkType = subplan->subLinkType;
      bool        useOr = subplan->useOr;
+     bool        isExpr = subplan->isExpr;
      MemoryContext oldcontext;
      TupleTableSlot *slot;
      Datum        result;
***************
*** 294,299 ****
--- 271,281 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 331,339 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 313,318 ----
***************
*** 351,448 ****
          found = true;

          /*
!          * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!          * operators for columns of tuple.
           */
!         plst = subplan->paramIds;
!         foreach(lst, node->exprs)
          {
!             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));

!             /*
!              * 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++;
          }

!         if (subLinkType == ANY_SUBLINK)
          {
!             /* 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;
          }
      }

--- 330,492 ----
          found = true;

          /*
!          * 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.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             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);
!
!             /* XXX this will need work if/when arrays support NULL elements */
!             if (!disnull)
              {
!                 if (subplan->elemtype != InvalidOid)
!                 {
!                     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
              {
!                 numelems = 1;
!                 dvalues = (Datum *) palloc(numelems * sizeof(Datum));
!                 dvalues[0] = (Datum) 0;
              }

          }

!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             /*
!              * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
!              * operators for columns of tuple.
!              */
!             col = 1;
!             plst = subplan->paramIds;
!             foreach(lst, node->exprs)
              {
!                 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++;
              }
!
!             if (subLinkType == ANY_SUBLINK)
              {
!                 /* 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;
              }
          }
      }

***************
*** 480,485 ****
--- 524,530 ----
  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,492 ****
--- 532,538 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 566,608 ****
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         int            col = 1;
          List       *plst;
          bool        isnew;

          /*
!          * 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;

-         /*
-          * 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)
          {
!             (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
!             node->havenullrows = true;
          }

          /*
--- 612,750 ----
      {
          HeapTuple    tup = slot->val;
          TupleDesc    tdesc = slot->ttc_tupleDescriptor;
!         TupleDesc    arrtdesc = NULL;
          List       *plst;
          bool        isnew;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          /*
!          * 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.
           */
!         if (!isExpr)
!             numelems = 1;
!         else
          {
!             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;
!             }

          }
!
!         for (elemnum = 0; elemnum < numelems; elemnum++)
          {
!             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,624 ****
--- 761,768 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
***************
*** 1098,1199 ****
          prm->execPlan = node;
          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);
  }
--- 1242,1245 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.252
diff -c -r1.252 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    6 Jun 2003 15:04:02 -0000    1.252
--- src/backend/nodes/copyfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 728,733 ****
--- 728,734 ----
      COPY_SCALAR_FIELD(agglevelsup);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
***************
*** 826,831 ****
--- 827,833 ----

      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,849 ****
--- 846,857 ----

      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);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.195
diff -c -r1.195 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    6 Jun 2003 15:04:02 -0000    1.195
--- src/backend/nodes/equalfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 205,210 ****
--- 205,211 ----
      COMPARE_SCALAR_FIELD(agglevelsup);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
***************
*** 301,306 ****
--- 302,308 ----
  {
      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,319 ****
--- 316,327 ----
  {
      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 */
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
retrieving revision 1.208
diff -c -r1.208 outfuncs.c
*** src/backend/nodes/outfuncs.c    15 Jun 2003 22:51:45 -0000    1.208
--- src/backend/nodes/outfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 616,621 ****
--- 616,622 ----
      WRITE_UINT_FIELD(agglevelsup);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
***************
*** 701,706 ****
--- 702,708 ----

      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,719 ****
--- 716,727 ----

      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);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
retrieving revision 1.154
diff -c -r1.154 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 Jun 2003 15:04:02 -0000    1.154
--- src/backend/nodes/readfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 416,421 ****
--- 416,422 ----
      READ_UINT_FIELD(agglevelsup);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
***************
*** 545,550 ****
--- 546,552 ----

      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);
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.76
diff -c -r1.76 subselect.c
*** src/backend/optimizer/plan/subselect.c    6 Jun 2003 15:04:02 -0000    1.76
--- src/backend/optimizer/plan/subselect.c    24 Jun 2003 02:24:06 -0000
***************
*** 83,89 ****

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    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);
--- 83,89 ----

  static List *convert_sublink_opers(List *lefthand, List *operOids,
                                     List *targetlist, int rtindex,
!                                    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,304 ****
--- 299,310 ----
       */
      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,380 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 380,386 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 457,463 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

          /*
--- 463,469 ----
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0, node->isExpr,
                                              &node->paramIds);

          /*
***************
*** 499,505 ****
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
--- 505,511 ----
  static List *
  convert_sublink_opers(List *lefthand, List *operOids,
                        List *targetlist, int rtindex,
!                       bool isExpr, List **righthandIds)
  {
      List       *result = NIL;
      List       *lst;
***************
*** 554,566 ****
           * 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));

          ReleaseSysCache(tup);

--- 560,597 ----
           * are not expecting to have to resolve unknown Params, so
           * it's okay to pass a null pstate.)
           */
!         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,683 ****
      /*
       * 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.)
       */
      if (sublink->subLinkType != ANY_SUBLINK)
          return NULL;
      if (length(sublink->operName) != 1 ||
          strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
          return NULL;
      /*
       * The sub-select must not refer to any Vars of the parent query.
       * (Vars of higher levels should be okay, though.)
--- 702,718 ----
      /*
       * 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.) 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,736 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 765,771 ----
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex, sublink->isExpr,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.139
diff -c -r1.139 clauses.c
*** src/backend/optimizer/util/clauses.c    6 Jun 2003 15:04:02 -0000    1.139
--- src/backend/optimizer/util/clauses.c    24 Jun 2003 02:24:06 -0000
***************
*** 133,138 ****
--- 133,160 ----
  }

  /*****************************************************************************
+  *              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
   *****************************************************************************/

Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.417
diff -c -r2.417 gram.y
*** src/backend/parser/gram.y    17 Jun 2003 23:12:36 -0000    2.417
--- src/backend/parser/gram.y    24 Jun 2003 02:24:06 -0000
***************
*** 5490,5495 ****
--- 5490,5496 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ANY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = makeList1(makeString("="));
                      n->subselect = $3;
***************
*** 5500,5505 ****
--- 5501,5507 ----
                      /* 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,5516 ****
--- 5513,5519 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = $3;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $4;
***************
*** 5521,5526 ****
--- 5524,5530 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = MULTIEXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = $1;
                      n->operName = $2;
                      n->subselect = $3;
***************
*** 5904,5909 ****
--- 5908,5914 ----
                      {
                              SubLink *n = (SubLink *)$3;
                              n->subLinkType = ANY_SUBLINK;
+                             n->isExpr = false;
                              n->lefthand = makeList1($1);
                              n->operName = makeList1(makeString("="));
                              $$ = (Node *)n;
***************
*** 5931,5936 ****
--- 5936,5942 ----
                      {
                          /* Make an IN node */
                          SubLink *n = (SubLink *)$4;
+                         n->isExpr = false;
                          n->subLinkType = ANY_SUBLINK;
                          n->lefthand = makeList1($1);
                          n->operName = makeList1(makeString("="));
***************
*** 5957,5967 ****
--- 5963,6000 ----
                  {
                      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,6543 ****
--- 6571,6577 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXPR_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $1;
***************
*** 6547,6552 ****
--- 6581,6587 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = EXISTS_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6556,6561 ****
--- 6591,6597 ----
                  {
                      SubLink *n = makeNode(SubLink);
                      n->subLinkType = ARRAY_SUBLINK;
+                     n->isExpr = false;
                      n->lefthand = NIL;
                      n->operName = NIL;
                      n->subselect = $2;
***************
*** 6730,6735 ****
--- 6766,6772 ----
  in_expr:    select_with_parens
                  {
                      SubLink *n = makeNode(SubLink);
+                     n->isExpr = false;
                      n->subselect = $1;
                      /* other fields will be filled later */
                      $$ = (Node *)n;
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
retrieving revision 2.97
diff -c -r2.97 parse_coerce.c
*** src/backend/parser/parse_coerce.c    26 May 2003 00:11:27 -0000    2.97
--- src/backend/parser/parse_coerce.c    24 Jun 2003 02:24:06 -0000
***************
*** 859,865 ****
      /* 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 (!OidIsValid(array_typelem))
              elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
                   format_type_be(array_typeid));
--- 859,869 ----
      /* Get the element type based on the array type, if we have one */
      if (OidIsValid(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,925 ****
      {
          if (!OidIsValid(array_typeid))
          {
!             array_typeid = get_array_type(elem_typeid);
              if (!OidIsValid(array_typeid))
                  elog(ERROR, "Cannot find array type for datatype %s",
                       format_type_be(elem_typeid));
--- 923,933 ----
      {
          if (!OidIsValid(array_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));
***************
*** 1169,1174 ****
--- 1177,1187 ----
      /* Somewhat-fast path for domain -> base type case */
      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,
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
retrieving revision 1.148
diff -c -r1.148 parse_expr.c
*** src/backend/parser/parse_expr.c    29 Apr 2003 22:13:10 -0000    1.148
--- src/backend/parser/parse_expr.c    24 Jun 2003 02:24:06 -0000
***************
*** 436,441 ****
--- 436,442 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else if (sublink->subLinkType == EXPR_SUBLINK ||
                           sublink->subLinkType == ARRAY_SUBLINK)
***************
*** 463,468 ****
--- 464,470 ----
                      sublink->operName = NIL;
                      sublink->operOids = NIL;
                      sublink->useOr = FALSE;
+                     sublink->isExpr = FALSE;
                  }
                  else
                  {
***************
*** 538,547 ****
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         optup = oper(op,
!                                      exprType(lexpr),
!                                      exprType((Node *) tent->expr),
!                                      false);
                          opform = (Form_pg_operator) GETSTRUCT(optup);

                          if (opform->oprresult != BOOLOID)
--- 540,569 ----
                           * here, because make_subplan() will insert type
                           * coercion calls if needed.
                           */
!                         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,749 ****
                          ArrayExpr  *e = (ArrayExpr *) lfirst(element);

                          if (!IsA(e, ArrayExpr))
!                             elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
                          if (ndims == 0)
                              ndims = e->ndims;
                          else if (e->ndims != ndims)
--- 765,771 ----
                          ArrayExpr  *e = (ArrayExpr *) lfirst(element);

                          if (!IsA(e, ArrayExpr))
!                             elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
                          if (ndims == 0)
                              ndims = e->ndims;
                          else if (e->ndims != ndims)
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
retrieving revision 1.149
diff -c -r1.149 parse_func.c
*** src/backend/parser/parse_func.c    6 Jun 2003 15:04:02 -0000    1.149
--- src/backend/parser/parse_func.c    24 Jun 2003 02:24:06 -0000
***************
*** 336,341 ****
--- 336,342 ----
          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);
Index: src/backend/parser/parse_oper.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
retrieving revision 1.64
diff -c -r1.64 parse_oper.c
*** src/backend/parser/parse_oper.c    26 May 2003 00:11:27 -0000    1.64
--- src/backend/parser/parse_oper.c    24 Jun 2003 02:24:06 -0000
***************
*** 137,142 ****
--- 137,169 ----
  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,180 ****
--- 202,234 ----
  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*
***************
*** 215,220 ****
--- 269,289 ----
      Oid            result;

      optup = equality_oper(argtype, false);
+     result = oprfuncid(optup);
+     ReleaseSysCache(optup);
+     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;
Index: src/backend/utils/adt/acl.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
retrieving revision 1.88
diff -c -r1.88 acl.c
*** src/backend/utils/adt/acl.c    11 Jun 2003 09:23:55 -0000    1.88
--- src/backend/utils/adt/acl.c    24 Jun 2003 02:24:06 -0000
***************
*** 427,432 ****
--- 427,441 ----
          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
Index: src/backend/utils/adt/array_userfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
retrieving revision 1.1
diff -c -r1.1 array_userfuncs.c
*** src/backend/utils/adt/array_userfuncs.c    8 Apr 2003 23:20:02 -0000    1.1
--- src/backend/utils/adt/array_userfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 18,52 ****
  #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
--- 18,23 ----
***************
*** 70,75 ****
--- 41,47 ----
      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,122 ****
          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)
      {
!         /* append newelem */
!         int    ub = dimv[0] + lb[0] - 1;
!         indx = ub + 1;
      }
      else
      {
!         /* prepend newelem */
!         indx = lb[0] - 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

!     result = array_set(v, 1, &indx, newelem, -1,
!                        typlen, typbyval, typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
--- 67,127 ----
          PG_RETURN_NULL();        /* keep compiler quiet */
      }

!     if (ARR_NDIM(v) == 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)
      {
!         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;
!     }
!
!     result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
!                                                  typalign, &isNull);

      PG_RETURN_ARRAYTYPE_P(result);
  }
***************
*** 145,157 ****

      /*
       * We must have one of the following combinations of inputs:
!      * 1) two arrays with ndims1 == ndims2
!      * 2) ndims1 == ndims2 - 1
!      * 3) ndims1 == ndims2 + 1
       */
      ndims1 = ARR_NDIM(v1);
      ndims2 = ARR_NDIM(v2);

      if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
          elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                      "%d dimensions", ndims1, ndims2);
--- 150,177 ----

      /*
       * We must have one of the following combinations of inputs:
!      * 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,412 ****
      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.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 286,300 ----
      PG_RETURN_ARRAYTYPE_P(result);
  }


  /*
!  * used by text_to_array() in varlena.c
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 303,309 ----
      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,435 ****
          lbs[i] = 1;
      }

!     get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

      return construct_md_array(dvalues, ndims, dims, lbs, element_type,
                                typlen, typbyval, typalign);
--- 318,352 ----
          lbs[i] = 1;
      }

!     /*
!      * 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);
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.89
diff -c -r1.89 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    9 May 2003 23:01:45 -0000    1.89
--- src/backend/utils/adt/arrayfuncs.c    24 Jun 2003 02:24:06 -0000
***************
*** 21,28 ****
--- 21,30 ----
  #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,85 ****

  #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,
--- 72,77 ----
***************
*** 93,102 ****
  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,
--- 85,90 ----
***************
*** 119,125 ****
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
!

  /*---------------------------------------------------------------------
   * array_in :
--- 107,113 ----
                     char *destPtr,
                     int *st, int *endp, char *srcPtr,
                     int typlen, bool typbyval, char typalign);
! static int array_cmp(FunctionCallInfo fcinfo);

  /*---------------------------------------------------------------------
   * array_in :
***************
*** 154,165 ****
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;

!     /* 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);

      /* Make a modifiable copy of the input */
      /* XXX why are we allocating an extra 2 bytes here? */
--- 142,190 ----
                  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;
!     }
!
!     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,647 ****
                  indx[MAXDIM];
      int            ndim,
                 *dim;

      element_type = ARR_ELEMTYPE(v);
!     system_cache_lookup(element_type, IOFunc_output,
!                         &typlen, &typbyval, &typdelim,
!                         &typelem, &typoutput, &typalign);
!     fmgr_info(typoutput, &outputproc);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 661,711 ----
                  indx[MAXDIM];
      int            ndim,
                 *dim;
+     ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * 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,805 ****
--- 864,870 ----
                  dim[MAXDIM],
                  lBound[MAXDIM];
      char        typalign;
+     ArrayMetaState *my_extra;

      /* Get the array header information */
      ndim = pq_getmsgint(buf, 4);
***************
*** 831,844 ****
          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);

      dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
                                typlen, typbyval, typalign,
--- 896,945 ----
          PG_RETURN_ARRAYTYPE_P(retval);
      }

!     /*
!      * 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,990 ****
      int            ndim,
                 *dim;
      StringInfoData buf;

      /* 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);

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
--- 1077,1130 ----
      int            ndim,
                 *dim;
      StringInfoData buf;
+     ArrayMetaState *my_extra;

      /* Get information about the element type and the array dimensions */
      element_type = ARR_ELEMTYPE(v);
!
!     /*
!      * 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,1481 ****
--- 1616,1641 ----
      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,1637 ****
--- 1792,1822 ----
      /* 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,1816 ****
--- 1996,2008 ----
      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,1839 ****
      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);

      /* Allocate temporary array for new values */
      values = (Datum *) palloc(nitems * sizeof(Datum));
--- 2021,2101 ----
      if (nitems <= 0)
          PG_RETURN_ARRAYTYPE_P(v);

!     /*
!      * 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,2056 ****
   *          compares two arrays for equality
   * result :
   *          returns true if the arrays are equal, false otherwise.
-  *
-  * XXX bitwise equality is pretty bogus ...
   *-----------------------------------------------------------------------------
   */
  Datum
--- 2311,2316 ----
***************
*** 2058,2069 ****
  {
      ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
      ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
      bool        result = true;

!     if (ARR_SIZE(array1) != ARR_SIZE(array2))
!         result = false;
!     else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
          result = false;

      /* Avoid leaking memory when handed toasted input. */
      PG_FREE_IF_COPY(array1, 0);
--- 2318,2435 ----
  {
      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;

!     /* 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,2125 ****
  }


! /***************************************************************************/
! /******************|          Support  Routines              |*****************/
! /***************************************************************************/

! static void
! system_cache_lookup(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);
  }

  /*
   * Fetch array element at pointer, converted correctly to a Datum
   */
--- 2439,2628 ----
  }


! /*-----------------------------------------------------------------------------
!  * 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)));
! }

! Datum
! array_lt(PG_FUNCTION_ARGS)
! {
!     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
! }
!
! Datum
! array_gt(PG_FUNCTION_ARGS)
! {
!     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,2428 ****
--- 2926,2943 ----
          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,2448 ****
      }

      /*
!      * If it's binary-compatible, return the array unmodified.
       */
      if (my_extra->coerce_finfo.fn_oid == InvalidOid)
!         PG_RETURN_ARRAYTYPE_P(src);

      /*
       * Use array_map to apply the function to each array element.
--- 2954,2969 ----
      }

      /*
!      * 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)
!     {
!         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.
***************
*** 2453,2456 ****
--- 2974,3092 ----
      locfcinfo.arg[0] = PointerGetDatum(src);

      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);
  }
Index: src/backend/utils/adt/varlena.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
retrieving revision 1.98
diff -c -r1.98 varlena.c
*** src/backend/utils/adt/varlena.c    15 May 2003 15:50:19 -0000    1.98
--- src/backend/utils/adt/varlena.c    24 Jun 2003 02:24:06 -0000
***************
*** 19,29 ****
--- 19,32 ----
  #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,1990 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

--- 1986,1992 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }

***************
*** 2004,2011 ****
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else
! /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
--- 2006,2012 ----
          if (fldnum == 1)        /* first field - just return the input
                                   * string */
              PG_RETURN_TEXT_P(inputstring);
!         else                    /* otherwise return an empty string */
              PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
      }
      else if ((start_posn != 0) && (end_posn == 0))
***************
*** 2026,2031 ****
--- 2027,2217 ----
          result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn -
fldsep_len,false); 
          PG_RETURN_TEXT_P(result_text);
      }
+ }
+
+ /*
+  * 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
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.96
diff -c -r1.96 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    22 Jun 2003 22:04:54 -0000    1.96
--- src/backend/utils/cache/lsyscache.c    24 Jun 2003 02:24:06 -0000
***************
*** 719,724 ****
--- 719,758 ----
  }

  /*
+  * 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.
   */
***************
*** 1088,1093 ****
--- 1122,1177 ----
      *typbyval = typtup->typbyval;
      *typalign = typtup->typalign;
      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
Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.68
diff -c -r1.68 fmgr.c
*** src/backend/utils/fmgr/fmgr.c    8 Apr 2003 23:20:02 -0000    1.68
--- src/backend/utils/fmgr/fmgr.c    24 Jun 2003 02:24:06 -0000
***************
*** 1673,1675 ****
--- 1673,1701 ----

      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;
+ }
Index: src/include/fmgr.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.27
diff -c -r1.27 fmgr.h
*** src/include/fmgr.h    8 Apr 2003 23:20:04 -0000    1.27
--- src/include/fmgr.h    24 Jun 2003 02:24:06 -0000
***************
*** 18,23 ****
--- 18,24 ----
  #ifndef FMGR_H
  #define FMGR_H

+ #include "nodes/nodes.h"

  /*
   * All functions that can be called directly by fmgr must have this signature.
***************
*** 372,385 ****
                   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);

  /*
   * Routines in dfmgr.c
--- 373,386 ----
                   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 get_fn_expr_functype(FunctionCallInfo fcinfo);

  /*
   * Routines in dfmgr.c
Index: src/include/catalog/pg_amop.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
retrieving revision 1.50
diff -c -r1.50 pg_amop.h
*** src/include/catalog/pg_amop.h    22 Jun 2003 22:04:55 -0000    1.50
--- src/include/catalog/pg_amop.h    24 Jun 2003 02:24:06 -0000
***************
*** 418,423 ****
--- 418,432 ----
  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
Index: src/include/catalog/pg_amproc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
retrieving revision 1.38
diff -c -r1.38 pg_amproc.h
*** src/include/catalog/pg_amproc.h    22 Jun 2003 22:04:55 -0000    1.38
--- src/include/catalog/pg_amproc.h    24 Jun 2003 02:35:24 -0000
***************
*** 78,83 ****
--- 78,84 ----


  /* btree */
+ DATA(insert (     397 1  382 ));
  DATA(insert (     421 1    357 ));
  DATA(insert (     423 1 1596 ));
  DATA(insert (     424 1 1693 ));
Index: src/include/catalog/pg_opclass.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
retrieving revision 1.51
diff -c -r1.51 pg_opclass.h
*** src/include/catalog/pg_opclass.h    22 Jun 2003 22:04:55 -0000    1.51
--- src/include/catalog/pg_opclass.h    24 Jun 2003 02:24:06 -0000
***************
*** 87,92 ****
--- 87,94 ----
   */

  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 ));
Index: src/include/catalog/pg_operator.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
retrieving revision 1.115
diff -c -r1.115 pg_operator.h
*** src/include/catalog/pg_operator.h    22 Jun 2003 22:04:55 -0000    1.115
--- src/include/catalog/pg_operator.h    24 Jun 2003 02:24:06 -0000
***************
*** 116,125 ****
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  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 = 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
eqseleqjoinsel )); 
--- 116,130 ----
  DATA(insert OID =  97 ( "<"           PGNSP PGUID b f    23    23    16 521 525     0     0     0     0 int4lt
scalarltselscalarltjoinsel )); 
  DATA(insert OID =  98 ( "="           PGNSP PGUID b t    25    25    16    98 531 664 664 664 666 texteq eqsel
eqjoinsel)); 

! 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
eqseleqjoinsel )); 
***************
*** 425,430 ****
--- 430,436 ----
  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 - - ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.304
diff -c -r1.304 pg_proc.h
*** src/include/catalog/pg_proc.h    22 Jun 2003 22:04:55 -0000    1.304
--- src/include/catalog/pg_proc.h    24 Jun 2003 02:35:46 -0000
***************
*** 758,763 ****
--- 758,765 ----
  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,1001 ****
  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 = 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_ ));
--- 990,1012 ----
  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 = 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,1027 ****
  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 = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
  DESCR("I/O");
--- 1017,1034 ----
  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 = 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 = 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,1327 ****
--- 1329,1336 ----
  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_ ));
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.83
diff -c -r1.83 primnodes.h
*** src/include/nodes/primnodes.h    6 Jun 2003 15:04:03 -0000    1.83
--- src/include/nodes/primnodes.h    24 Jun 2003 02:24:06 -0000
***************
*** 226,231 ****
--- 226,232 ----
      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,372 ****
  /* ----------------
   * 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:
   *    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 ...)
   * 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
--- 359,377 ----
  /* ----------------
   * SubLink
   *
!  * 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,420 ****
--- 420,427 ----
      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,461 ****
--- 463,477 ----
      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 */
Index: src/include/optimizer/clauses.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
retrieving revision 1.63
diff -c -r1.63 clauses.h
*** src/include/optimizer/clauses.h    28 May 2003 16:04:02 -0000    1.63
--- src/include/optimizer/clauses.h    24 Jun 2003 02:24:06 -0000
***************
*** 28,33 ****
--- 28,36 ----
  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);
Index: src/include/parser/parse_oper.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
retrieving revision 1.25
diff -c -r1.25 parse_oper.h
*** src/include/parser/parse_oper.h    29 Apr 2003 22:13:11 -0000    1.25
--- src/include/parser/parse_oper.h    24 Jun 2003 02:24:06 -0000
***************
*** 44,49 ****
--- 44,50 ----
  /* 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 */
Index: src/include/utils/acl.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
retrieving revision 1.52
diff -c -r1.52 acl.h
*** src/include/utils/acl.h    11 Jun 2003 09:23:55 -0000    1.52
--- src/include/utils/acl.h    24 Jun 2003 02:24:06 -0000
***************
*** 192,197 ****
--- 192,198 ----
  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
Index: src/include/utils/array.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
retrieving revision 1.38
diff -c -r1.38 array.h
*** src/include/utils/array.h    8 May 2003 22:19:57 -0000    1.38
--- src/include/utils/array.h    24 Jun 2003 02:24:06 -0000
***************
*** 32,37 ****
--- 32,68 ----
      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,96 ****
  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_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,
--- 117,131 ----
  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_type_coerce(PG_FUNCTION_ARGS);

  extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 159,172 ----
                    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,152 ****
  /*
   * 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,
                                           Datum element,
                                           int ndims);

--- 183,193 ----
  /*
   * prototypes for functions defined in array_userfuncs.c
   */
  extern Datum array_push(PG_FUNCTION_ARGS);
  extern Datum array_cat(PG_FUNCTION_ARGS);

! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
!                                          Oid element_type,
                                           Datum element,
                                           int ndims);

Index: src/include/utils/builtins.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.219
diff -c -r1.219 builtins.h
*** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
--- src/include/utils/builtins.h    24 Jun 2003 02:24:06 -0000
***************
*** 530,535 ****
--- 530,537 ----
                        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);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
retrieving revision 1.71
diff -c -r1.71 lsyscache.h
*** src/include/utils/lsyscache.h    22 Jun 2003 22:04:55 -0000    1.71
--- src/include/utils/lsyscache.h    24 Jun 2003 02:24:06 -0000
***************
*** 15,20 ****
--- 15,29 ----

  #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,46 ****
--- 50,56 ----
  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,61 ****
--- 66,79 ----
  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);
Index: src/interfaces/ecpg/preproc/preproc.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
retrieving revision 1.236
diff -c -r1.236 preproc.y
*** src/interfaces/ecpg/preproc/preproc.y    20 Jun 2003 13:36:34 -0000    1.236
--- src/interfaces/ecpg/preproc/preproc.y    24 Jun 2003 02:24:06 -0000
***************
*** 4595,4601 ****
                  $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");

              types = this;
          }
--- 4595,4601 ----
                  $3.type_enum != ECPGt_char &&
                      $3.type_enum != ECPGt_unsigned_char &&
                  atoi(this->type->type_index) >= 0)
!                 mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");

              types = this;
          }
***************
*** 5415,5421 ****
                      $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");

                  types = this;
              }
--- 5415,5421 ----
                      $5.type_enum != ECPGt_char &&
                      $5.type_enum != ECPGt_unsigned_char &&
                      atoi(this->type->type_index) >= 0)
!                     mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");

                  types = this;
              }
***************
*** 5482,5488 ****

                      default:
                          if (atoi(length) >= 0)
!                             mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data
types");

                          if (atoi(dimension) < 0)
                              type = ECPGmake_simple_type($5.type_enum, make_str("1"));
--- 5482,5488 ----

                      default:
                          if (atoi(length) >= 0)
!                             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"));
Index: src/interfaces/ecpg/preproc/type.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
retrieving revision 1.52
diff -c -r1.52 type.c
*** src/interfaces/ecpg/preproc/type.c    20 Jun 2003 12:00:59 -0000    1.52
--- src/interfaces/ecpg/preproc/type.c    24 Jun 2003 02:24:06 -0000
***************
*** 504,510 ****
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multi-dimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
--- 504,510 ----
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multidimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
Index: src/interfaces/ecpg/preproc/variable.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
retrieving revision 1.21
diff -c -r1.21 variable.c
*** src/interfaces/ecpg/preproc/variable.c    11 Jun 2003 06:39:13 -0000    1.21
--- src/interfaces/ecpg/preproc/variable.c    24 Jun 2003 02:24:06 -0000
***************
*** 436,442 ****
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

          *length = type_index;
      }
--- 436,442 ----
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

          *length = type_index;
      }
***************
*** 444,450 ****
      if (atoi(type_dimension) >= 0)
      {
          if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

          if (atoi(*dimension) >= 0)
              *length = *dimension;
--- 444,450 ----
      if (atoi(type_dimension) >= 0)
      {
          if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

          if (atoi(*dimension) >= 0)
              *length = *dimension;
***************
*** 463,472 ****
          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");

      if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
!         mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

      switch (type_enum)
      {
--- 463,472 ----
          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 multidimensional array support");

      if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
!         mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");

      switch (type_enum)
      {
***************
*** 480,486 ****
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");

              break;
          case ECPGt_varchar:
--- 480,486 ----
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");

              break;
          case ECPGt_varchar:
***************
*** 525,531 ****
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");

              break;
      }
--- 525,531 ----
              }

              if (atoi(*length) >= 0)
!                 mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");

              break;
      }
Index: src/test/regress/expected/arrays.out
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
retrieving revision 1.11
diff -c -r1.11 arrays.out
*** src/test/regress/expected/arrays.out    8 Apr 2003 23:20:04 -0000    1.11
--- src/test/regress/expected/arrays.out    24 Jun 2003 02:24:06 -0000
***************
*** 178,196 ****
  (1 row)

  -- functions
! SELECT singleton_array(42) AS "{42}";
!  {42}
! ------
!  {42}
! (1 row)
!
! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
   {42,6}
  --------
   {42,6}
  (1 row)

! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
   {6,42}
  --------
   {6,42}
--- 178,190 ----
  (1 row)

  -- functions
! SELECT array_append(array[42], 6) AS "{42,6}";
   {42,6}
  --------
   {42,6}
  (1 row)

! SELECT array_prepend(6, array[42]) AS "{6,42}";
   {6,42}
  --------
   {6,42}
***************
*** 212,235 ****
   {{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
--- 206,211 ----
Index: src/test/regress/sql/arrays.sql
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
retrieving revision 1.10
diff -c -r1.10 arrays.sql
*** src/test/regress/sql/arrays.sql    8 Apr 2003 23:20:04 -0000    1.10
--- src/test/regress/sql/arrays.sql    24 Jun 2003 02:24:06 -0000
***************
*** 130,144 ****
  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_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]]];
--- 130,140 ----
  SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";

  -- functions
! 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}}";

  -- operators
  SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: Patch to be verbose about being unable to read ~/.pgpasss...
Next
From: Bruce Momjian
Date:
Subject: Re: array support patch phase 1 patch