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 3ECD6887.3000104@joeconway.com
Whole thread Raw
In response to Re: array support patch phase 1 patch  (Joe Conway <mail@joeconway.com>)
Responses Re: array support patch phase 1 patch  (Joe Conway <mail@joeconway.com>)
List pgsql-patches
Joe Conway wrote:
> Attached is an updated version of the doc patch along with the addition
> of two new functions, as previously discussed:
>
>    - str_to_array(str TEXT, delim TEXT) returns TEXT[]
>    - array_to_str(array ANYARRAY, delim TEXT) returns TEXT
>
> The patch is fairly large because:
>
>    1. the documentation part of it is large
>    2. I moved some static functions around and made them accessible so
>       that I could use them in implementing the two functions.
>
> It passes all regression tests, and as mentioned above includes
> documentation.
>
> If there are no objections, please apply.

The attached is an update to the one above, with the following additions
     (recommended by Tom here -
     http://archives.postgresql.org/pgsql-patches/2003-04/msg00030.php):

1. array_eq() now applies the element type's equality operator on an
    element-by-element basis.
2. applied Tom's cache lookup results caching technique in arrayfuncs.c,
    array_userfuncs.c, and in the new functions in varlena.c.

Still passes all regression tests. No additional doc updates needed for
this one. If there are no objections, please apply.

Thanks,

Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.25
diff -c -r1.25 array.sgml
*** doc/src/sgml/array.sgml    13 Mar 2003 01:30:26 -0000    1.25
--- doc/src/sgml/array.sgml    22 May 2003 23:42:01 -0000
***************
*** 9,15 ****

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multidimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

--- 9,15 ----

   <para>
    <productname>PostgreSQL</productname> allows columns of a table to be
!   defined as variable-length multi-dimensional arrays. Arrays of any
    built-in type or user-defined type can be created.
   </para>

***************
*** 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, multi-dimesion 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:  Multiple dimension 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>
***************
*** 169,175 ****
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multidimensional arrays.
   </para>

   <para>
--- 272,278 ----
    value currently has 4 elements, it will have five elements after an
    update that assigns to <literal>array[5]</>.  Currently, enlargement in
    this fashion is only allowed for one-dimensional arrays, not
!   multi-dimensional arrays.
   </para>

   <para>
***************
*** 179,184 ****
--- 282,367 ----
   </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 multi-dimensional arrays.
+   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>
+
+   <function>array_prepend</function> and <function>array_append</function>
+   work with a one-dimensional array and a single element to be pushed on
+   to the beginning or end of the array, respectively. 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 array_prepend(1, ARRAY[2,3]) AS f) AS t;
+  array_dims
+ ------------
+  [0:2]
+ (1 row)
+ </programlisting>
+
+   <function>array_cat</function> works with either two
+   <replaceable>n</>-dimension arrays, or an <replaceable>n</>-dimension
+   and an <replaceable>n+1</> dimension array. In the former case, the two
+   <replaceable>n</>-dimension arrays become outer elements of an
+   <replaceable>n+1</> dimension array. In the latter, the
+   <replaceable>n</>-dimension array is added as either the first or last
+   outer element of the <replaceable>n+1</> dimension array.
+  </para>
+
+  <para>
+   A final method of enlarging arrays is through the concatenation operator,
+   <command>||</command>, which works exactly as <function>array_cat</function>
+   does.
+ <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>
+  </para>
+
+  <para>
    The syntax for <command>CREATE TABLE</command> allows fixed-length
    arrays to be defined:

***************
*** 194,199 ****
--- 377,392 ----
   </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
***************
*** 292,298 ****
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multidimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
--- 485,491 ----
     for the array's element type.  (Among the standard data types provided
     in the <productname>PostgreSQL</productname> distribution, type
     <literal>box</> uses a semicolon (<literal>;</>) but all the others
!    use comma.)  In a multi-dimensional array, each dimension (row, plane,
     cube, etc.) gets its own level of curly braces, and delimiters
     must be written between adjacent curly-braced entities of the same level.
     You may write whitespace before a left brace, after a right
***************
*** 300,305 ****
--- 493,564 ----
     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 multi-dimensional 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 ****
--- 575,588 ----
     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    22 May 2003 23:42:01 -0000
***************
*** 6962,6967 ****
--- 6962,7224 ----

    </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_accum</function>
+       (<type>anyarray</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      append an element to the end of an array, ignoring
+      <literal>NULL</literal> elements, and creating an array if needed
+     </entry>
+     <entry><literal>array_accum(null, 1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <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_assign</function>
+       (<type>anyarray</type>, <type>integer</type>, <type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      assign a value to a specific array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_assign(ARRAY[1,2,3], 2, 99)</literal></entry>
+     <entry><literal>{1,99,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_subscript</function>
+       (<type>anyarray</type>, <type>integer</type>)
+      </literal>
+     </entry>
+     <entry><type>anyelement</type></entry>
+     <entry>
+      returns requested array element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>array_subscript(ARRAY[1,2,3], 3)</literal></entry>
+     <entry><literal>3</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>array_to_str</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_str(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>singleton_array</function>
+       (<type>anyelement</type>)
+      </literal>
+     </entry>
+     <entry><type>anyarray</type></entry>
+     <entry>
+      create an array from the provided element, returning
+      <literal>NULL</literal> for <literal>NULL</literal> inputs
+     </entry>
+     <entry><literal>singleton_array(1)</literal></entry>
+     <entry><literal>{1}</literal></entry>
+        </row>
+        <row>
+     <entry>
+      <literal>
+       <function>str_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>str_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/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    22 May 2003 23:42:01 -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 ----
***************
*** 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);
  }
--- 1060,1063 ----
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    23 May 2003 00:05:38 -0000
***************
*** 42,48 ****
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
--- 42,48 ----
      else
          ndims = 1;

!     PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, elem_type,
                                                   PG_GETARG_DATUM(0),
                                                   ndims));
  }
***************
*** 70,75 ****
--- 70,76 ----
      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");
***************
*** 113,119 ****
          indx = lb[0] - 1;
      }

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

      result = array_set(v, 1, &indx, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
--- 114,148 ----
          indx = lb[0] - 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;
!     }

      result = array_set(v, 1, &indx, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 293,299 ****
          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));
      }
--- 322,328 ----
          if (tgt_elem_type == InvalidOid)
              elog(ERROR, "Target type is not an array");

!         PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, tgt_elem_type,
                                                       PG_GETARG_DATUM(1),
                                                       1));
      }
***************
*** 329,334 ****
--- 358,364 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx_to_chg = PG_GETARG_INT32(1);
***************
*** 349,355 ****
      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);
--- 379,413 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * 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, &idx_to_chg, newelem, -1,
                         typlen, typbyval, typalign, &isNull);
***************
*** 375,380 ****
--- 433,439 ----
      int16        typlen;
      bool        typbyval;
      char        typalign;
+     ArrayMetaState *my_extra;

      v = PG_GETARG_ARRAYTYPE_P(0);
      idx = PG_GETARG_INT32(1);
***************
*** 394,400 ****
      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);

--- 453,487 ----
      if (element_type == 0)
          elog(ERROR, "Invalid array element type: %u", element_type);

!     /*
!      * 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_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);

***************
*** 406,412 ****
   * given a null input array.
   */
  ArrayType *
! create_singleton_array(Oid element_type, Datum element, int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
--- 493,502 ----
   * given a null input array.
   */
  ArrayType *
! create_singleton_array(FunctionCallInfo fcinfo,
!                        Oid element_type,
!                        Datum element,
!                        int ndims)
  {
      Datum    dvalues[1];
      int16    typlen;
***************
*** 415,420 ****
--- 505,511 ----
      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);
--- 520,554 ----
          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    22 May 2003 23:42:01 -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 ----
***************
*** 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);
***************
*** 1811,1816 ****
--- 1951,1963 ----
      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));
--- 1976,2056 ----
      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
--- 2266,2271 ----
***************
*** 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);
--- 2273,2390 ----
  {
      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   *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 *) fmgr_info->fn_extra;
+         if (my_extra == NULL)
+         {
+             fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
+                                                      sizeof(ArrayMetaState));
+             my_extra = (ArrayMetaState *) 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, 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);
***************
*** 2077,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
   */
--- 2398,2403 ----
***************
*** 2453,2456 ****
--- 2731,2849 ----
      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    23 May 2003 00:11:27 -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.94
diff -c -r1.94 lsyscache.c
*** src/backend/utils/cache/lsyscache.c    13 May 2003 04:38:58 -0000    1.94
--- src/backend/utils/cache/lsyscache.c    22 May 2003 23:42:01 -0000
***************
*** 969,974 ****
--- 969,1024 ----
      ReleaseSysCache(tp);
  }

+ /*
+  * get_type_metadata
+  *
+  *        A six-fer:    given the type OID, return typlen, typbyval, typalign,
+  *                    typdelim, typelem, IO function Oid. The IO function
+  *                    returned is controlled by IOFuncSelector
+  */
+ void
+ get_type_metadata(Oid element_type,
+                     IOFuncSelector which_func,
+                     int *typlen,
+                     bool *typbyval,
+                     char *typdelim,
+                     Oid *typelem,
+                     Oid *proc,
+                     char *typalign)
+ {
+     HeapTuple    typeTuple;
+     Form_pg_type typeStruct;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(element_type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "cache lookup failed for type %u", element_type);
+     typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+     *typlen = typeStruct->typlen;
+     *typbyval = typeStruct->typbyval;
+     *typdelim = typeStruct->typdelim;
+     *typelem = typeStruct->typelem;
+     *typalign = typeStruct->typalign;
+     switch (which_func)
+     {
+         case IOFunc_input:
+             *proc = typeStruct->typinput;
+             break;
+         case IOFunc_output:
+             *proc = typeStruct->typoutput;
+             break;
+         case IOFunc_receive:
+             *proc = typeStruct->typreceive;
+             break;
+         case IOFunc_send:
+             *proc = typeStruct->typsend;
+             break;
+     }
+     ReleaseSysCache(typeTuple);
+ }
+
  #ifdef NOT_USED
  char
  get_typalign(Oid typid)
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    22 May 2003 23:42:01 -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    22 May 2003 23:42:01 -0000
***************
*** 377,385 ****
   * 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
--- 377,386 ----
   * 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_proc.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.300
diff -c -r1.300 pg_proc.h
*** src/include/catalog/pg_proc.h    15 May 2003 15:50:19 -0000    1.300
--- src/include/catalog/pg_proc.h    22 May 2003 23:42:01 -0000
***************
*** 1016,1021 ****
--- 1016,1025 ----
  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 = 385 (  str_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 = 386 (  array_to_str       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");
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    23 May 2003 00:06:49 -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
   */
***************
*** 124,130 ****
                    Oid elmtype,
                    int elmlen, bool elmbyval, char elmalign,
                    Datum **elemsp, int *nelemsp);
!

  /*
   * prototypes for functions defined in arrayutils.c
--- 155,168 ----
                    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
***************
*** 146,152 ****
  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);

--- 184,191 ----
  extern Datum array_accum(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.217
diff -c -r1.217 builtins.h
*** src/include/utils/builtins.h    15 May 2003 15:50:20 -0000    1.217
--- src/include/utils/builtins.h    22 May 2003 23:42:01 -0000
***************
*** 539,544 ****
--- 539,546 ----
                        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.69
diff -c -r1.69 lsyscache.h
*** src/include/utils/lsyscache.h    9 May 2003 18:08:48 -0000    1.69
--- src/include/utils/lsyscache.h    22 May 2003 23:42:01 -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 char *get_attname(Oid relid, AttrNumber attnum);
***************
*** 53,58 ****
--- 62,75 ----
  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);

pgsql-patches by date:

Previous
From: Fernando Nasser
Date:
Subject: JDBC: Better initial capacity for StringBuffers reduces memory usage
Next
From: Rod Taylor
Date:
Subject: pgstattuple for schemas