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 3EE41700.2070202@joeconway.com
Whole thread Raw
In response to Re: array support patch phase 1 patch  (Peter Eisentraut <peter_e@gmx.net>)
Responses Re: array support patch phase 1 patch
Re: array support patch phase 1 patch
List pgsql-patches
Peter Eisentraut wrote:
[...much snipping follows...]
> No, the analogues would be "unidimensional", "bidimensional", and
> "many-dimensional".  There difference is that you close up prefixes (which
> are not words by themselves), but you hyphenate compounds of independent
> words.
>
>>>The function array_subscript() should be removed from code and
>>>The function array_assign() should be removed from code and documentation.
>>>The function singleton_array() should be removed from code and
>>
>>>The function array_append() should be documented as being equivalent to
>>>'array || element' and specifically intended for user-defined aggregate
>>>functions.
>>
>>That's fine. I would add similar descriptions for array_prepend() and
>>array_cat().
>
> Sounds good.  My main concern is that people will have a clear view of
> what's standard and recommended for which situation, so that support will
> be easier and users won't be confronted with a long list of equivalent,
> undifferentiated options.
>

The attached patch addresses Peter's concerns, subject to our agreements
above. I.e, the changes are:

1) All instances of "multi-dimensional array" and "multiple dimension
array" throughout the source tree have been changed to "multidimensional
array". Even if everyone doesn't agree this is correct, it is at least
consistent throughout, so if we later decide to change it back, it will
be easier ;-)

2) The functions singleton_array(), array_assign(), and
array_subscript() have been removed along with all references thereof.

3) The documentation emphasizes use of the concatenation operator '||'
as preferred over the functions array_append(), array_prepend(), and
array_cat(). A mention of direct use of the functions in user-defined
aggregates is also included.

[...more snipage from a different part of the thread...]
Tom Lane wrote:
 > BTW, it might be better for array_cmp to insist that the array element
 > type have a default btree opclass, and use the comparison proc from
 > that opclass in place of equality_oper() and ordering_oper().  This'd
 > be faster (only one call per comparison) as well as more semantically
 > pure.

I stopped short of this change because there doesn't seem to be a way to
get the btree opclass tuple given just the opcintype without scanning
pg_opclass. Am I missing something? Or should I just not worry about it
and scan pg_opclass?

 > I've got some other problems with this patch (I still don't understand
 > what the hacking on aggregates is supposed to accomplish, for example)
 > but the array comparison stuff seems relatively straightforward.

As I said in an earlier message, I believe that I have implemented this
correctly. Is there a specific corner case not covered? I'd really like
to see some form of polymorphic function support for aggregates --
please let me know what I can do to make that happen.

Joe

p.s. I added Bruce to the thread because he asked me about the status of
the patch, and because the lists have been so slow the last few days.
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    9 Jun 2003 02:13:32 -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    9 Jun 2003 01:53:35 -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    9 Jun 2003 01:31:10 -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    9 Jun 2003 01:31:10 -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    9 Jun 2003 01:42:58 -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.105
diff -c -r1.105 nodeAgg.c
*** src/backend/executor/nodeAgg.c    30 May 2003 20:23:10 -0000    1.105
--- src/backend/executor/nodeAgg.c    9 Jun 2003 01:31:10 -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 ----
***************
*** 1176,1182 ****
--- 1175,1195 ----
          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;

          /* Look for a previous duplicate aggregate */
***************
*** 1224,1229 ****
--- 1237,1402 ----
                          &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.
***************
*** 1236,1249 ****
              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
--- 1409,1415 ----
              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
***************
*** 1454,1457 ****
--- 1620,1656 ----
      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.45
diff -c -r1.45 nodeSubplan.c
*** src/backend/executor/nodeSubplan.c    8 Apr 2003 23:20:01 -0000    1.45
--- src/backend/executor/nodeSubplan.c    9 Jun 2003 01:31:10 -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;
***************
*** 292,297 ****
--- 269,279 ----
          bool        rownull = false;
          int            col = 1;
          List       *plst;
+         int            numelems;
+         int            elemnum;
+         Datum        dvalue;
+         Datum       *dvalues = NULL;
+         bool        disnull;

          if (subLinkType == EXISTS_SUBLINK)
          {
***************
*** 329,337 ****

          if (subLinkType == ARRAY_SUBLINK)
          {
-             Datum    dvalue;
-             bool    disnull;
-
              found = true;
              /* stash away current value */
              dvalue = heap_getattr(tup, 1, tdesc, &disnull);
--- 311,316 ----
***************
*** 349,446 ****
          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;
          }
      }

--- 328,490 ----
          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;
              }
          }
      }

***************
*** 478,483 ****
--- 522,528 ----
  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;
***************
*** 485,490 ****
--- 530,536 ----
      MemoryContext oldcontext;
      int            nbuckets;
      TupleTableSlot *slot;
+     TupleTableSlot *arrslot = NULL;

      Assert(subplan->subLinkType == ANY_SUBLINK);
      Assert(!subplan->useOr);
***************
*** 562,604 ****
      {
          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;
          }

          /*
--- 608,746 ----
      {
          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;
!             }
          }

          /*
***************
*** 615,620 ****
--- 757,764 ----
       * have the potential for a double free attempt.
       */
      ExecClearTuple(node->projRight->pi_slot);
+     if (arrslot)
+         ExecClearTuple(arrslot);

      MemoryContextSwitchTo(oldcontext);
  }
***************
*** 1084,1185 ****
          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);
  }
--- 1228,1231 ----
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.251
diff -c -r1.251 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    28 May 2003 16:03:56 -0000    1.251
--- src/backend/nodes/copyfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 727,732 ****
--- 727,733 ----
      COPY_NODE_FIELD(target);
      COPY_SCALAR_FIELD(aggstar);
      COPY_SCALAR_FIELD(aggdistinct);
+     COPY_NODE_FIELD(args);

      return newnode;
  }
***************
*** 825,830 ****
--- 826,832 ----

      COPY_SCALAR_FIELD(subLinkType);
      COPY_SCALAR_FIELD(useOr);
+     COPY_SCALAR_FIELD(isExpr);
      COPY_NODE_FIELD(lefthand);
      COPY_NODE_FIELD(operName);
      COPY_OIDLIST_FIELD(operOids);
***************
*** 843,848 ****
--- 845,856 ----

      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.194
diff -c -r1.194 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    28 May 2003 16:03:56 -0000    1.194
--- src/backend/nodes/equalfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 204,209 ****
--- 204,210 ----
      COMPARE_NODE_FIELD(target);
      COMPARE_SCALAR_FIELD(aggstar);
      COMPARE_SCALAR_FIELD(aggdistinct);
+     COMPARE_NODE_FIELD(args);

      return true;
  }
***************
*** 300,305 ****
--- 301,307 ----
  {
      COMPARE_SCALAR_FIELD(subLinkType);
      COMPARE_SCALAR_FIELD(useOr);
+     COMPARE_SCALAR_FIELD(isExpr);
      COMPARE_NODE_FIELD(lefthand);
      COMPARE_NODE_FIELD(operName);
      COMPARE_OIDLIST_FIELD(operOids);
***************
*** 313,318 ****
--- 315,326 ----
  {
      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.206
diff -c -r1.206 outfuncs.c
*** src/backend/nodes/outfuncs.c    28 May 2003 16:03:56 -0000    1.206
--- src/backend/nodes/outfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 615,620 ****
--- 615,621 ----
      WRITE_NODE_FIELD(target);
      WRITE_BOOL_FIELD(aggstar);
      WRITE_BOOL_FIELD(aggdistinct);
+     WRITE_NODE_FIELD(args);
  }

  static void
***************
*** 700,705 ****
--- 701,707 ----

      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);
***************
*** 713,718 ****
--- 715,726 ----

      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.153
diff -c -r1.153 readfuncs.c
*** src/backend/nodes/readfuncs.c    6 May 2003 00:20:32 -0000    1.153
--- src/backend/nodes/readfuncs.c    9 Jun 2003 01:31:10 -0000
***************
*** 415,420 ****
--- 415,421 ----
      READ_NODE_FIELD(target);
      READ_BOOL_FIELD(aggstar);
      READ_BOOL_FIELD(aggdistinct);
+     READ_NODE_FIELD(args);

      READ_DONE();
  }
***************
*** 544,549 ****
--- 545,551 ----

      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.75
diff -c -r1.75 subselect.c
*** src/backend/optimizer/plan/subselect.c    29 Apr 2003 22:13:09 -0000    1.75
--- src/backend/optimizer/plan/subselect.c    9 Jun 2003 01:31:10 -0000
***************
*** 67,73 ****

  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);
--- 67,73 ----

  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);
***************
*** 240,245 ****
--- 240,251 ----
       */
      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;
***************
*** 316,322 ****
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
--- 322,328 ----
          exprs = convert_sublink_opers(lefthand,
                                        slink->operOids,
                                        plan->targetlist,
!                                       0, node->isExpr,
                                        &node->paramIds);
          node->setParam = listCopy(node->paramIds);
          PlannerInitPlan = lappend(PlannerInitPlan, node);
***************
*** 399,405 ****
          node->exprs = convert_sublink_opers(lefthand,
                                              slink->operOids,
                                              plan->targetlist,
!                                             0,
                                              &node->paramIds);

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

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

--- 505,542 ----
           * 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);

***************
*** 616,628 ****
      /*
       * 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.)
--- 647,663 ----
      /*
       * 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.)
***************
*** 675,681 ****
      exprs = convert_sublink_opers(sublink->lefthand,
                                    sublink->operOids,
                                    subselect->targetList,
!                                   rtindex,
                                    &ininfo->sub_targetlist);
      return (Node *) make_ands_explicit(exprs);
  }
--- 710,716 ----
      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.138
diff -c -r1.138 clauses.c
*** src/backend/optimizer/util/clauses.c    28 May 2003 22:32:49 -0000    1.138
--- src/backend/optimizer/util/clauses.c    9 Jun 2003 01:31:10 -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.416
diff -c -r2.416 gram.y
*** src/backend/parser/gram.y    29 May 2003 20:40:36 -0000    2.416
--- src/backend/parser/gram.y    9 Jun 2003 01:31:10 -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    9 Jun 2003 01:31:10 -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    9 Jun 2003 01:42:58 -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.148
diff -c -r1.148 parse_func.c
*** src/backend/parser/parse_func.c    26 May 2003 00:11:27 -0000    1.148
--- src/backend/parser/parse_func.c    9 Jun 2003 01:31:10 -0000
***************
*** 335,340 ****
--- 335,341 ----
          aggref->target = lfirst(fargs);
          aggref->aggstar = agg_star;
          aggref->aggdistinct = agg_distinct;
+         aggref->args = fargs;

          retval = (Node *) 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    9 Jun 2003 01:31:10 -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.86
diff -c -r1.86 acl.c
*** src/backend/utils/adt/acl.c    24 Jan 2003 21:53:29 -0000    1.86
--- src/backend/utils/adt/acl.c    9 Jun 2003 01:31:10 -0000
***************
*** 720,732 ****
      aidat = ACL_DAT(acl);
      for (i = 0; i < num; ++i)
      {
!         if (aip->ai_grantee == aidat[i].ai_grantee &&
!             aip->ai_privs == aidat[i].ai_privs)
              PG_RETURN_BOOL(true);
      }
      PG_RETURN_BOOL(false);
  }


  /*
   * has_table_privilege variants
--- 720,740 ----
      aidat = ACL_DAT(acl);
      for (i = 0; i < num; ++i)
      {
!         if (aclitemeq(aip, &aidat[i]))
              PG_RETURN_BOOL(true);
      }
      PG_RETURN_BOOL(false);
  }

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

  /*
   * has_table_privilege variants
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    9 Jun 2003 01:46:03 -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    9 Jun 2003 01:31:10 -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    9 Jun 2003 01:31:10 -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.95
diff -c -r1.95 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    26 May 2003 00:11:27 -0000    1.95
--- src/backend/utils/cache/lsyscache.c    9 Jun 2003 01:31:10 -0000
***************
*** 625,630 ****
--- 625,664 ----
  }

  /*
+  * 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.
   */
***************
*** 994,999 ****
--- 1028,1083 ----
      *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    9 Jun 2003 01:31:10 -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    9 Jun 2003 01:31:10 -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.49
diff -c -r1.49 pg_amop.h
*** src/include/catalog/pg_amop.h    26 May 2003 00:11:27 -0000    1.49
--- src/include/catalog/pg_amop.h    9 Jun 2003 01:31:10 -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.37
diff -c -r1.37 pg_amproc.h
*** src/include/catalog/pg_amproc.h    26 May 2003 00:11:27 -0000    1.37
--- src/include/catalog/pg_amproc.h    9 Jun 2003 01:31:10 -0000
***************
*** 78,83 ****
--- 78,84 ----


  /* btree */
+ DATA(insert (     397 1  398 ));
  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.50
diff -c -r1.50 pg_opclass.h
*** src/include/catalog/pg_opclass.h    28 May 2003 16:04:00 -0000    1.50
--- src/include/catalog/pg_opclass.h    9 Jun 2003 02:48:16 -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.114
diff -c -r1.114 pg_operator.h
*** src/include/catalog/pg_operator.h    26 May 2003 00:11:27 -0000    1.114
--- src/include/catalog/pg_operator.h    9 Jun 2003 01:31:10 -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 t    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 t    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.302
diff -c -r1.302 pg_proc.h
*** src/include/catalog/pg_proc.h    26 May 2003 00:11:27 -0000    1.302
--- src/include/catalog/pg_proc.h    9 Jun 2003 01:52:28 -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 = 398 (  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");
***************
*** 984,997 ****
  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_ ));
--- 986,1008 ----
  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_ ));
***************
*** 1002,1023 ****
  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");
--- 1013,1030 ----
  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");
***************
*** 1318,1323 ****
--- 1325,1332 ----
  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 = 1038 (  seteval           PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
  DESCR("internal function supporting PostQuel-style sets");
  DATA(insert OID = 1044 (  bpcharin           PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" bpcharin - _null_ ));
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
retrieving revision 1.82
diff -c -r1.82 primnodes.h
*** src/include/nodes/primnodes.h    6 May 2003 00:20:33 -0000    1.82
--- src/include/nodes/primnodes.h    9 Jun 2003 01:31:10 -0000
***************
*** 225,230 ****
--- 225,231 ----
      Expr       *target;            /* expression we are aggregating on */
      bool        aggstar;        /* TRUE if argument was really '*' */
      bool        aggdistinct;    /* TRUE if it's agg(DISTINCT ...) */
+     List       *args;            /* arguments to the aggregate */
  } Aggref;

  /* ----------------
***************
*** 357,371 ****
  /* ----------------
   * 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
--- 358,376 ----
  /* ----------------
   * 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
***************
*** 414,419 ****
--- 419,426 ----
      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 */
***************
*** 455,460 ****
--- 462,476 ----
      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    9 Jun 2003 01:31:10 -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    9 Jun 2003 01:31:10 -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.51
diff -c -r1.51 acl.h
*** src/include/utils/acl.h    23 Jan 2003 23:39:07 -0000    1.51
--- src/include/utils/acl.h    9 Jun 2003 01:31:10 -0000
***************
*** 188,193 ****
--- 188,194 ----
  extern Datum aclinsert(PG_FUNCTION_ARGS);
  extern Datum aclremove(PG_FUNCTION_ARGS);
  extern Datum aclcontains(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    9 Jun 2003 01:47:03 -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    9 Jun 2003 01:31:10 -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.70
diff -c -r1.70 lsyscache.h
*** src/include/utils/lsyscache.h    26 May 2003 00:11:28 -0000    1.70
--- src/include/utils/lsyscache.h    9 Jun 2003 01:31:10 -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);
***************
*** 39,44 ****
--- 48,54 ----
  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);
***************
*** 54,59 ****
--- 64,77 ----
  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.227
diff -c -r1.227 preproc.y
*** src/interfaces/ecpg/preproc/preproc.y    30 May 2003 13:22:02 -0000    1.227
--- src/interfaces/ecpg/preproc/preproc.y    9 Jun 2003 01:42:58 -0000
***************
*** 4549,4555 ****
                  $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;
          }
--- 4549,4555 ----
                  $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;
          }
***************
*** 5372,5378 ****
                      $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;
              }
--- 5372,5378 ----
                      $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;
              }
***************
*** 5439,5445 ****

                      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"));
--- 5439,5445 ----

                      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.51
diff -c -r1.51 type.c
*** src/interfaces/ecpg/preproc/type.c    29 May 2003 13:59:26 -0000    1.51
--- src/interfaces/ecpg/preproc/type.c    9 Jun 2003 01:42:58 -0000
***************
*** 493,499 ****
                  switch (type->u.element->type)
                  {
                      case ECPGt_array:
!                         yyerror("internal error, found multi-dimensional array\n");
                          break;
                      case ECPGt_struct:
                      case ECPGt_union:
--- 493,499 ----
                  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.20
diff -c -r1.20 variable.c
*** src/interfaces/ecpg/preproc/variable.c    29 May 2003 13:59:26 -0000    1.20
--- src/interfaces/ecpg/preproc/variable.c    9 Jun 2003 01:42:58 -0000
***************
*** 405,411 ****
      if (atoi(type_index) >= 0)
      {
          if (atoi(*length) >= 0)
!             mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");

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

          *length = type_index;
      }
***************
*** 413,419 ****
      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;
--- 413,419 ----
      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;
***************
*** 432,441 ****
          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)
      {
--- 432,441 ----
          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)
      {
***************
*** 449,455 ****
              }

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

              break;
          case ECPGt_varchar:
--- 449,455 ----
              }

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

              break;
          case ECPGt_varchar:
***************
*** 494,500 ****
              }

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

              break;
      }
--- 494,500 ----
              }

              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    9 Jun 2003 04:43:05 -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    9 Jun 2003 04:42:20 -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: [BUGS] Detecting proper bison version before make
Next
From: Kurt Roeckx
Date:
Subject: Re: Ipv6 network cleanup patch #2.