Create real array comparison functions (that use the element datatype's
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 27 Jun 2003 00:33:26 +0000 (00:33 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 27 Jun 2003 00:33:26 +0000 (00:33 +0000)
comparison functions), replacing the highly bogus bitwise array_eq.  Create
a btree index opclass for ANYARRAY --- it is now possible to create indexes
on array columns.
Arrange to cache the results of catalog lookups across multiple array
operations, instead of repeating the lookups on every call.
Add string_to_array and array_to_string functions.
Remove singleton_array, array_accum, array_assign, and array_subscript
functions, since these were for proof-of-concept and not intended to become
supported functions.
Minor adjustments to behavior in some corner cases with empty or
zero-dimensional arrays.

Joe Conway (with some editorializing by Tom Lane).

28 files changed:
doc/src/sgml/array.sgml
doc/src/sgml/func.sgml
src/backend/executor/execQual.c
src/backend/executor/nodeSubplan.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_oper.c
src/backend/utils/adt/acl.c
src/backend/utils/adt/array_userfuncs.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/varlena.c
src/backend/utils/cache/lsyscache.c
src/include/catalog/catversion.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_proc.h
src/include/parser/parse_oper.h
src/include/utils/acl.h
src/include/utils/array.h
src/include/utils/builtins.h
src/include/utils/lsyscache.h
src/interfaces/ecpg/preproc/preproc.y
src/interfaces/ecpg/preproc/type.c
src/interfaces/ecpg/preproc/variable.c
src/test/regress/expected/arrays.out
src/test/regress/sql/arrays.sql

index 985a92f7fa1b31b58e9ea413865fbc437881820b..a0c93aedeb1de78ee985c92e06d27439acb32f8b 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.27 2003/06/25 21:30:25 momjian Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.28 2003/06/27 00:33:25 tgl Exp $ -->
 
 <sect1 id="arrays">
  <title>Arrays</title>
@@ -60,14 +60,74 @@ INSERT INTO sal_emp
 </programlisting>
  </para>
 
+ <para>
+  A limitation of the present array implementation is that individual
+  elements of an array cannot be SQL null values.  The entire array can be set
+  to null, but you can't have an array with some elements null and some
+  not.
+ </para>
+ <para>
+  This can lead to surprising results. For example, the result of the
+  previous two inserts looks like this:
+<programlisting>
+SELECT * FROM sal_emp;
+ name  |      pay_by_quarter       |      schedule
+-------+---------------------------+--------------------
+ Bill  | {10000,10000,10000,10000} | {{meeting},{""}}
+ Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
+(2 rows)
+</programlisting>
+  Because the <literal>[2][2]</literal> element of
+  <structfield>schedule</structfield> is missing in each of the
+  <command>INSERT</command> statements, the <literal>[1][2]</literal>
+  element is discarded.
+ </para>
+
  <note>
   <para>
-   A limitation of the present array implementation is that individual
-   elements of an array cannot be SQL null values.  The entire array can be set
-   to null, but you can't have an array with some elements null and some
-   not.  Fixing this is on the to-do list.
+   Fixing this is on the to-do list.
   </para>
  </note>
+
+ <para>
+  The <command>ARRAY</command> expression syntax may also be used:
+<programlisting>
+INSERT INTO sal_emp
+    VALUES ('Bill',
+    ARRAY[10000, 10000, 10000, 10000],
+    ARRAY[['meeting', 'lunch'], ['','']]);
+
+INSERT INTO sal_emp
+    VALUES ('Carol',
+    ARRAY[20000, 25000, 25000, 25000],
+    ARRAY[['talk', 'consult'], ['meeting', '']]);
+SELECT * FROM sal_emp;
+ name  |      pay_by_quarter       |           schedule
+-------+---------------------------+-------------------------------
+ Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
+ Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
+(2 rows)
+</programlisting>
+  Note that with this syntax, multidimensional arrays must have matching
+  extents for each dimension. This eliminates the missing-array-elements
+  problem above. For example:
+<programlisting>
+INSERT INTO sal_emp
+    VALUES ('Carol',
+    ARRAY[20000, 25000, 25000, 25000],
+    ARRAY[['talk', 'consult'], ['meeting']]);
+ERROR:  Multidimensional arrays must have array expressions with matching dimensions
+</programlisting>
+  Also notice that string literals are single quoted instead of double quoted.
+ </para>
+
+ <note>
+  <para>
+   The examples in the rest of this section are based on the
+   <command>ARRAY</command> expression syntax <command>INSERT</command>s.
+  </para>
+ </note>
+
  </sect2>
 
  <sect2>
@@ -132,11 +192,30 @@ SELECT schedule[1:2][1] FROM sal_emp WHERE name = 'Bill';
 </programlisting>
 
   with the same result.  An array subscripting operation is always taken to
-  represent an array slice if any of the subscripts are written in the
-  form
+  represent an array slice if any of the subscripts are written in the form
   <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
   A lower bound of 1 is assumed for any subscript where only one value
-  is specified.
+  is specified.  Another example follows:
+<programlisting>
+SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
+         schedule
+---------------------------
+ {{meeting,lunch},{"",""}}
+(1 row)
+</programlisting>
+ </para>
+
+ <para>
+  Additionally, we can also access a single arbitrary array element of 
+  a one-dimensional array with the <function>array_subscript</function>
+  function:
+<programlisting>
+SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
+ array_subscript
+-----------------
+           10000
+(1 row)
+</programlisting>
  </para>
 
  <para>
@@ -147,7 +226,23 @@ UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}'
     WHERE name = 'Carol';
 </programlisting>
 
-  or updated at a single element:
+  or using the <command>ARRAY</command> expression syntax:
+
+<programlisting>
+UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
+    WHERE name = 'Carol';
+</programlisting>
+
+  <note>
+   <para>
+    Anywhere you can use the <quote>curly braces</quote> array syntax,
+    you can also use the <command>ARRAY</command> expression syntax. The
+    remainder of this section will illustrate only one or the other, but
+    not both.
+   </para>
+  </note>
+
+  An array may also be updated at a single element:
 
 <programlisting>
 UPDATE sal_emp SET pay_by_quarter[4] = 15000
@@ -160,6 +255,14 @@ UPDATE sal_emp SET pay_by_quarter[4] = 15000
 UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
     WHERE name = 'Carol';
 </programlisting>
+
+  A one-dimensional array may also be updated with the
+  <function>array_assign</function> function:
+
+<programlisting>
+UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
+    WHERE name = 'Bill';
+</programListing>
  </para>
 
  <para>
@@ -178,6 +281,88 @@ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
   create an array with subscript values running from -2 to 7.
  </para>
 
+ <para>
+  An array can also be enlarged by using the concatenation operator,
+  <command>||</command>.
+<programlisting>
+SELECT ARRAY[1,2] || ARRAY[3,4];
+   ?column?
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
+      ?column?
+---------------------
+ {{5,6},{1,2},{3,4}}
+(1 row)
+</programlisting>
+
+  The concatenation operator allows a single element to be pushed on to the
+  beginning or end of a one-dimensional array. It also allows two
+  <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
+  and an <replaceable>N+1</>-dimensional array. In the former case, the two
+  <replaceable>N</>-dimension arrays become outer elements of an
+  <replaceable>N+1</>-dimensional array. In the latter, the
+  <replaceable>N</>-dimensional array is added as either the first or last
+  outer element of the <replaceable>N+1</>-dimensional array.
+
+  The array is extended in the direction of the push. Hence, by pushing
+  onto the beginning of an array with a one-based subscript, a zero-based
+  subscript array is created:
+
+<programlisting>
+SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
+ array_dims
+------------
+ [0:2]
+(1 row)
+</programlisting>
+ </para>
+
+ <para>
+  An array can also be enlarged by using the functions
+  <function>array_prepend</function>, <function>array_append</function>,
+  or <function>array_cat</function>. The first two only support one-dimensional
+  arrays, but <function>array_cat</function> supports multidimensional arrays.
+
+  Note that the concatenation operator discussed above is preferred over
+  direct use of these functions. In fact, the functions are primarily for use
+  in implementing the concatenation operator. However, they may be directly
+  useful in the creation of user-defined aggregates. Some examples:
+
+<programlisting>
+SELECT array_prepend(1, ARRAY[2,3]);
+ array_prepend
+---------------
+ {1,2,3}
+(1 row)
+
+SELECT array_append(ARRAY[1,2], 3);
+ array_append
+--------------
+ {1,2,3}
+(1 row)
+
+SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
+   array_cat
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
+      array_cat
+---------------------
+ {{1,2},{3,4},{5,6}}
+(1 row)
+
+SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
+      array_cat
+---------------------
+ {{5,6},{1,2},{3,4}}
+</programlisting>
+ </para>
+
  <para>
   The syntax for <command>CREATE TABLE</command> allows fixed-length
   arrays to be defined:
@@ -193,6 +378,16 @@ CREATE TABLE tictactoe (
   length.
  </para>
 
+ <para>
+  An alternative syntax for one-dimensional arrays may be used.
+  <structfield>pay_by_quarter</structfield> could have been defined as:
+<programlisting>
+    pay_by_quarter  integer ARRAY[4],
+</programlisting>
+  This syntax may <emphasis>only</emphasis> be used with the integer
+  constant to denote the array size.
+ </para>
+
  <para>
   Actually, the current implementation does not enforce the declared
   number of dimensions either.  Arrays of a particular element type are
@@ -300,6 +495,72 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
    is not ignored, however: after skipping leading whitespace, everything
    up to the next right brace or delimiter is taken as the item value.
   </para>
+
+  <para>
+   As illustrated earlier in this chapter, arrays may also be represented
+   using the <command>ARRAY</command> expression syntax. This representation
+   of an array value consists of items that are interpreted according to the
+   I/O conversion rules for the array's element type, plus decoration that
+   indicates the array structure. The decoration consists of the keyword
+   <command>ARRAY</command> and square brackets (<literal>[</> and
+   <literal>]</>) around the array values, plus delimiter characters between
+   adjacent items. The delimiter character is always a comma (<literal>,</>).
+   When representing multidimensional arrays, the keyword
+   <command>ARRAY</command> is only necessary for the outer level. For example,
+   <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
+<programlisting>
+SELECT ARRAY[['hello world', 'happy birthday']];
+               array
+------------------------------------
+ {{"hello world","happy birthday"}}
+(1 row)
+</programlisting>
+  or it also could be written as:
+<programlisting>
+SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
+               array
+------------------------------------
+ {{"hello world","happy birthday"}}
+(1 row)
+</programlisting>
+  </para>
+
+  <para>
+   A final method to represent an array, is through an
+   <command>ARRAY</command> sub-select expression. For example:
+<programlisting>
+SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
+                          ?column?
+-------------------------------------------------------------
+ {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
+(1 row)
+</programlisting>
+  The sub-select may <emphasis>only</emphasis> return a single column. The
+  resulting one-dimensional array will have an element for each row in the
+  sub-select result, with an element type matching that of the sub-select's
+  target column.
+  </para>
+
+  <para>
+   Arrays may be cast from one type to another in similar fashion to other
+   data types:
+
+<programlisting>
+SELECT ARRAY[1,2,3]::oid[];
+  array
+---------
+ {1,2,3}
+(1 row)
+
+SELECT CAST(ARRAY[1,2,3] AS float8[]);
+  array
+---------
+ {1,2,3}
+(1 row)
+</programlisting>
+
+  </para>
+
  </sect2>
 
  <sect2>
@@ -317,6 +578,14 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
    that would otherwise be taken as array syntax or ignorable white space.
   </para>
 
+ <note>
+  <para>
+   The discussion in the preceding paragraph with respect to double quoting does
+   not pertain to the <command>ARRAY</command> expression syntax. In that case,
+   each element is quoted exactly as any other literal value of the element type.
+  </para>
+ </note>
+
   <para>
    The array output routine will put double quotes around element values
    if they are empty strings or contain curly braces, delimiter characters,
index 3db3ab34e83a84193b129235212d9e0e9b7782ad..df677df37a227432d47e8dd32fe0ba18d2d28aa3 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.156 2003/06/25 21:30:25 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.157 2003/06/27 00:33:25 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -6962,6 +6962,203 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
 
   </sect1>
 
+ <sect1 id="functions-array">
+  <title>Array Functions</title>
+
+  <para>
+   <xref linkend="array-operators-table"> shows the operators
+   available for the <type>array</type> types.
+  </para>
+
+    <table id="array-operators-table">
+     <title><type>array</type> Operators</title>
+     <tgroup cols="4">
+      <thead>
+       <row>
+   <entry>Operator</entry>
+   <entry>Description</entry>
+   <entry>Example</entry>
+   <entry>Result</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+   <entry> <literal>=</literal> </entry>
+   <entry>equals</entry>
+   <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
+   <entry><literal>t</literal></entry>
+       </row>
+       <row>
+   <entry> <literal>||</literal> </entry>
+   <entry>array-to-array concatenation</entry>
+   <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
+   <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+       </row>
+       <row>
+   <entry> <literal>||</literal> </entry>
+   <entry>array-to-array concatenation</entry>
+   <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
+   <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
+       </row>
+       <row>
+   <entry> <literal>||</literal> </entry>
+   <entry>element-to-array concatenation</entry>
+   <entry><literal>3 || ARRAY[4,5,6]</literal></entry>
+   <entry><literal>{3,4,5,6}</literal></entry>
+       </row>
+       <row>
+   <entry> <literal>||</literal> </entry>
+   <entry>array-to-element concatenation</entry>
+   <entry><literal>ARRAY[4,5,6] || 7</literal></entry>
+   <entry><literal>{4,5,6,7}</literal></entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+
+  <para>
+   <xref linkend="array-functions-table"> shows the functions
+   available for use with array types. See <xref linkend="arrays">
+   for more discussion and examples for the use of these functions.
+  </para>
+
+    <table id="array-functions-table">
+     <title><type>array</type> Functions</title>
+     <tgroup cols="5">
+      <thead>
+       <row>
+   <entry>Function</entry>
+   <entry>Return Type</entry>
+   <entry>Description</entry>
+   <entry>Example</entry>
+   <entry>Result</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+   <entry>
+     <literal>
+      <function>array_append</function>
+      (<type>anyarray</type>, <type>anyelement</type>)
+     </literal>
+    </entry>
+   <entry><type>anyarray</type></entry>
+   <entry>
+     append an element to the end of an array, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+   <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
+   <entry><literal>{1,2,3}</literal></entry>
+       </row>
+       <row>
+   <entry>
+     <literal>
+      <function>array_cat</function>
+      (<type>anyarray</type>, <type>anyarray</type>)
+     </literal>
+    </entry>
+   <entry><type>anyarray</type></entry>
+   <entry>
+     concatenate two arrays, returning <literal>NULL</literal>
+     for <literal>NULL</literal> inputs
+    </entry>
+   <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
+   <entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
+       </row>
+       <row>
+   <entry>
+     <literal>
+      <function>array_dims</function>
+      (<type>anyarray</type>)
+     </literal>
+    </entry>
+   <entry><type>text</type></entry>
+   <entry>
+     returns a text representation of array dimension lower and upper bounds,
+     generating an ERROR for <literal>NULL</literal> inputs
+    </entry>
+   <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
+   <entry><literal>[1:2][1:3]</literal></entry>
+       </row>
+       <row>
+   <entry>
+     <literal>
+      <function>array_lower</function>
+      (<type>anyarray</type>, <type>integer</type>)
+     </literal>
+    </entry>
+   <entry><type>integer</type></entry>
+   <entry>
+     returns lower bound of the requested array dimension, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+   <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
+   <entry><literal>0</literal></entry>
+       </row>
+       <row>
+   <entry>
+     <literal>
+      <function>array_prepend</function>
+      (<type>anyelement</type>, <type>anyarray</type>)
+     </literal>
+    </entry>
+   <entry><type>anyarray</type></entry>
+   <entry>
+     append an element to the beginning of an array, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+   <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
+   <entry><literal>{1,2,3}</literal></entry>
+       </row>
+       <row>
+   <entry>
+     <literal>
+      <function>array_to_string</function>
+      (<type>anyarray</type>, <type>text</type>)
+     </literal>
+    </entry>
+   <entry><type>text</type></entry>
+   <entry>
+     concatenates array elements using provided delimiter, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+   <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
+   <entry><literal>1.10~^~2.20~^~3.30</literal></entry>
+       </row>
+       <row>
+   <entry>
+     <literal>
+      <function>array_upper</function>
+      (<type>anyarray</type>, <type>integer</type>)
+     </literal>
+    </entry>
+   <entry><type>integer</type></entry>
+   <entry>
+     returns upper bound of the requested array dimension, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+   <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
+   <entry><literal>4</literal></entry>
+       </row>
+       <row>
+   <entry>
+     <literal>
+      <function>string_to_array</function>
+      (<type>text</type>, <type>text</type>)
+     </literal>
+    </entry>
+   <entry><type>text[]</type></entry>
+   <entry>
+     splits string into array elements using provided delimiter, returning
+     <literal>NULL</literal> for <literal>NULL</literal> inputs
+    </entry>
+   <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
+   <entry><literal>{1.1,2.2,3.3}</literal></entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+  </sect1>
 
  <sect1 id="functions-aggregate">
   <title>Aggregate Functions</title>
index 6e0a46e0e7fbf696f91a469a5ad7a45b44db00cd..ee29c195ae72def772515ec820851b89b2842488 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.132 2003/06/25 21:30:28 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.133 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1528,17 +1528,17 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
            {
                /* Check other sub-arrays are compatible */
                if (elem_ndims != ARR_NDIM(array))
-                   elog(ERROR, "Multiple dimension arrays must have array "
+                   elog(ERROR, "Multidimensional arrays must have array "
                         "expressions with matching number of dimensions");
 
                if (memcmp(elem_dims, ARR_DIMS(array),
                           elem_ndims * sizeof(int)) != 0)
-                   elog(ERROR, "Multiple dimension arrays must have array "
+                   elog(ERROR, "Multidimensional arrays must have array "
                         "expressions with matching dimensions");
 
                if (memcmp(elem_lbs, ARR_LBOUND(array),
                           elem_ndims * sizeof(int)) != 0)
-                   elog(ERROR, "Multiple dimension arrays must have array "
+                   elog(ERROR, "Multidimensional arrays must have array "
                         "expressions with matching dimensions");
            }
 
index 645673776841b340b941dcc4bf8a49b1063daca3..5999ef707705473dcad243d7bbbd0407e2d65522 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.49 2003/06/25 21:30:29 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.50 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/lsyscache.h"
 
 
-typedef struct ArrayBuildState
-{
-   MemoryContext mcontext;     /* where all the temp stuff is kept */
-   Datum      *dvalues;        /* array of accumulated Datums */
-   /*
-    * The allocated size of dvalues[] is always a multiple of
-    * ARRAY_ELEMS_CHUNKSIZE
-    */
-#define ARRAY_ELEMS_CHUNKSIZE  64
-   int         nelems;         /* number of valid Datums in dvalues[] */
-   Oid         element_type;   /* data type of the Datums */
-   int16       typlen;         /* needed info about datatype */
-   bool        typbyval;
-   char        typalign;
-} ArrayBuildState;
-
 static Datum ExecHashSubPlan(SubPlanState *node,
                             ExprContext *econtext,
                             bool *isNull);
@@ -54,12 +38,6 @@ static Datum ExecScanSubPlan(SubPlanState *node,
 static void buildSubPlanHash(SubPlanState *node);
 static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
 static bool tupleAllNulls(HeapTuple tuple);
-static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
-                                        Datum dvalue, bool disnull,
-                                        Oid element_type,
-                                        MemoryContext rcontext);
-static Datum makeArrayResult(ArrayBuildState *astate,
-                            MemoryContext rcontext);
 
 
 /* ----------------------------------------------------------------
@@ -1099,101 +1077,3 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
        parent->chgParam = bms_add_member(parent->chgParam, paramid);
    }
 }
-
-/*
- * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
- *
- * astate is working state (NULL on first call)
- * rcontext is where to keep working state
- */
-static ArrayBuildState *
-accumArrayResult(ArrayBuildState *astate,
-                Datum dvalue, bool disnull,
-                Oid element_type,
-                MemoryContext rcontext)
-{
-   MemoryContext arr_context,
-                 oldcontext;
-
-   if (astate == NULL)
-   {
-       /* First time through --- initialize */
-
-       /* Make a temporary context to hold all the junk */
-       arr_context = AllocSetContextCreate(rcontext,
-                                           "ARRAY_SUBLINK Result",
-                                           ALLOCSET_DEFAULT_MINSIZE,
-                                           ALLOCSET_DEFAULT_INITSIZE,
-                                           ALLOCSET_DEFAULT_MAXSIZE);
-       oldcontext = MemoryContextSwitchTo(arr_context);
-       astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
-       astate->mcontext = arr_context;
-       astate->dvalues = (Datum *)
-           palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
-       astate->nelems = 0;
-       astate->element_type = element_type;
-       get_typlenbyvalalign(element_type,
-                            &astate->typlen,
-                            &astate->typbyval,
-                            &astate->typalign);
-   }
-   else
-   {
-       oldcontext = MemoryContextSwitchTo(astate->mcontext);
-       Assert(astate->element_type == element_type);
-       /* enlarge dvalues[] if needed */
-       if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
-           astate->dvalues = (Datum *)
-               repalloc(astate->dvalues,
-                        (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
-   }
-
-   if (disnull)
-       elog(ERROR, "NULL elements not allowed in Arrays");
-
-   /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
-   astate->dvalues[astate->nelems++] =
-       datumCopy(dvalue, astate->typbyval, astate->typlen);
-
-   MemoryContextSwitchTo(oldcontext);
-
-   return astate;
-}
-
-/*
- * makeArrayResult - produce final result of ARRAY_SUBLINK
- *
- * astate is working state (not NULL)
- * rcontext is where to construct result
- */
-static Datum
-makeArrayResult(ArrayBuildState *astate,
-               MemoryContext rcontext)
-{
-   ArrayType  *result;
-   int         dims[1];
-   int         lbs[1];
-   MemoryContext oldcontext;
-
-   /* Build the final array result in rcontext */
-   oldcontext = MemoryContextSwitchTo(rcontext);
-
-   dims[0] = astate->nelems;
-   lbs[0] = 1;
-
-   result = construct_md_array(astate->dvalues,
-                               1,
-                               dims,
-                               lbs,
-                               astate->element_type,
-                               astate->typlen,
-                               astate->typbyval,
-                               astate->typalign);
-
-   MemoryContextSwitchTo(oldcontext);
-
-   /* Clean up all the junk */
-   MemoryContextDelete(astate->mcontext);
-
-   return PointerGetDatum(result);
-}
index 987d129027fc68602685a7774270b80ff38ce578..eba6f5a13334d907606dae7b6ac74e8b4536eb6e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.100 2003/06/25 21:30:31 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.101 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1145,7 +1145,8 @@ IsPreferredType(CATEGORY category, Oid type)
  * invokable, no-function-needed pg_cast entry.  Also, a domain is always
  * binary-coercible to its base type, though *not* vice versa (in the other
  * direction, one must apply domain constraint checks before accepting the
- * value as legitimate).
+ * value as legitimate).  We also need to special-case the polymorphic
+ * ANYARRAY type.
  *
  * This function replaces IsBinaryCompatible(), which was an inherently
  * symmetric test.  Since the pg_cast entries aren't necessarily symmetric,
@@ -1170,6 +1171,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
    if (srctype == targettype)
        return true;
 
+   /* Also accept any array type as coercible to ANYARRAY */
+   if (targettype == ANYARRAYOID)
+       if (get_element_type(srctype) != InvalidOid)
+           return true;
+
    /* Else look in pg_cast */
    tuple = SearchSysCache(CASTSOURCETARGET,
                           ObjectIdGetDatum(srctype),
index 1e655217103a0fe79e294c994940277315fc73b5..415242bc7a8c67d5ffa50c705515dfbb9c6c6339 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.150 2003/06/25 21:30:31 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.151 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -743,7 +743,7 @@ transformExpr(ParseState *pstate, Node *expr)
                        ArrayExpr  *e = (ArrayExpr *) lfirst(element);
 
                        if (!IsA(e, ArrayExpr))
-                           elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
+                           elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
                        if (ndims == 0)
                            ndims = e->ndims;
                        else if (e->ndims != ndims)
index 69edb4b85bd0ba144292070730dd7d44d3084141..983041eb45bfc806813356a5a1c97374febf01d7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.66 2003/06/25 21:30:32 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.67 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,28 +137,50 @@ Operator
 equality_oper(Oid argtype, bool noError)
 {
    Operator    optup;
+   Oid         elem_type;
 
    /*
-    * Look for an "=" operator for the 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.
+    * If the datatype is an array, then we can use array_eq ... but only
+    * if there is a suitable equality operator for the element type.
+    * (We must run this test first, since compatible_oper will find
+    * array_eq, but would not notice the lack of an element operator.)
     */
-   optup = compatible_oper(makeList1(makeString("=")),
-                           argtype, argtype, true);
-   if (optup != NULL)
+   elem_type = get_element_type(argtype);
+   if (OidIsValid(elem_type))
+   {
+       optup = equality_oper(elem_type, true);
+       if (optup != NULL)
+       {
+           ReleaseSysCache(optup);
+           return SearchSysCache(OPEROID,
+                                 ObjectIdGetDatum(ARRAY_EQ_OP),
+                                 0, 0, 0);
+       }
+   }
+   else
    {
        /*
-        * Only believe that it's equality if it's mergejoinable,
-        * hashjoinable, or uses eqsel() as oprrest.
+        * Look for an "=" operator for the 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.
         */
-       Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(optup);
+       optup = compatible_oper(makeList1(makeString("=")),
+                               argtype, argtype, true);
+       if (optup != NULL)
+       {
+           /*
+            * Only believe that it's equality if it's mergejoinable,
+            * hashjoinable, or uses eqsel() as oprrest.
+            */
+           Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(optup);
 
-       if (OidIsValid(pgopform->oprlsortop) ||
-           pgopform->oprcanhash ||
-           pgopform->oprrest == F_EQSEL)
-           return optup;
+           if (OidIsValid(pgopform->oprlsortop) ||
+               pgopform->oprcanhash ||
+               pgopform->oprrest == F_EQSEL)
+               return optup;
 
-       ReleaseSysCache(optup);
+           ReleaseSysCache(optup);
+       }
    }
    if (!noError)
        elog(ERROR, "Unable to identify an equality operator for type %s",
@@ -175,27 +197,50 @@ Operator
 ordering_oper(Oid argtype, bool noError)
 {
    Operator    optup;
+   Oid         elem_type;
 
    /*
-    * Find the 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.
+    * If the datatype is an array, then we can use array_lt ... but only
+    * if there is a suitable ordering operator for the element type.
+    * (We must run this test first, since the code below would find
+    * array_lt if there's an element = operator, but would not notice the
+    * lack of an element < operator.)
     */
-   optup = equality_oper(argtype, noError);
-   if (optup != NULL)
+   elem_type = get_element_type(argtype);
+   if (OidIsValid(elem_type))
    {
-       Oid     lsortop = ((Form_pg_operator) GETSTRUCT(optup))->oprlsortop;
-
-       ReleaseSysCache(optup);
-
-       if (OidIsValid(lsortop))
+       optup = ordering_oper(elem_type, true);
+       if (optup != NULL)
        {
-           optup = SearchSysCache(OPEROID,
-                                  ObjectIdGetDatum(lsortop),
-                                  0, 0, 0);
-           if (optup != NULL)
-               return optup;
+           ReleaseSysCache(optup);
+           return SearchSysCache(OPEROID,
+                                 ObjectIdGetDatum(ARRAY_LT_OP),
+                                 0, 0, 0);
+       }
+   }
+   else
+   {
+       /*
+        * Find the 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 = equality_oper(argtype, noError);
+       if (optup != NULL)
+       {
+           Oid     lsortop;
+
+           lsortop = ((Form_pg_operator) GETSTRUCT(optup))->oprlsortop;
+           ReleaseSysCache(optup);
+           if (OidIsValid(lsortop))
+           {
+               optup = SearchSysCache(OPEROID,
+                                      ObjectIdGetDatum(lsortop),
+                                      0, 0, 0);
+               if (optup != NULL)
+                   return optup;
+           }
        }
    }
    if (!noError)
@@ -237,6 +282,21 @@ ordering_oper_opid(Oid argtype)
    return result;
 }
 
+/*
+ * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+ */
+Oid
+ordering_oper_funcid(Oid argtype)
+{
+   Operator    optup;
+   Oid         result;
+
+   optup = ordering_oper(argtype, false);
+   result = oprfuncid(optup);
+   ReleaseSysCache(optup);
+   return result;
+}
+
 
 /* given operator tuple, return the operator OID */
 Oid
index 415a086b7dd180458322db64d97e18dbb422aa62..878f4ebe2a11053de2226b9bf616a3aeff712723 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.90 2003/06/25 21:30:32 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.91 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@ static const char *getid(const char *s, char *n);
 static void putid(char *p, const char *s);
 static Acl *allocacl(int n);
 static const char *aclparse(const char *s, AclItem *aip);
-static bool aclitemeq(const AclItem *a1, const AclItem *a2);
+static bool aclitem_match(const AclItem *a1, const AclItem *a2);
 static Acl *recursive_revoke(Acl *acl, AclId grantee,
                             AclMode revoke_privs, DropBehavior behavior);
 
@@ -415,18 +415,33 @@ aclitemout(PG_FUNCTION_ARGS)
 }
 
 /*
- * aclitemeq
- *     Two AclItems are considered equal iff they have the same
+ * aclitem_match
+ *     Two AclItems are considered to match iff they have the same
  *     grantee and grantor; the privileges are ignored.
  */
 static bool
-aclitemeq(const AclItem *a1, const AclItem *a2)
+aclitem_match(const AclItem *a1, const AclItem *a2)
 {
    return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
        a1->ai_grantee == a2->ai_grantee &&
        a1->ai_grantor == a2->ai_grantor;
 }
 
+/*
+ * aclitem equality operator
+ */
+Datum
+aclitem_eq(PG_FUNCTION_ARGS)
+{
+   AclItem    *a1 = PG_GETARG_ACLITEM_P(0);
+   AclItem    *a2 = PG_GETARG_ACLITEM_P(1);
+   bool        result;
+
+   result = a1->ai_privs == a2->ai_privs &&
+       a1->ai_grantee == a2->ai_grantee &&
+       a1->ai_grantor == a2->ai_grantor;
+   PG_RETURN_BOOL(result);
+}
 
 /*
  * acldefault()  --- create an ACL describing default access permissions
@@ -535,7 +550,7 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBeh
 
    for (dst = 0; dst < num; ++dst)
    {
-       if (aclitemeq(mod_aip, old_aip + dst))
+       if (aclitem_match(mod_aip, old_aip + dst))
        {
            /* found a match, so modify existing item */
            new_acl = allocacl(num);
@@ -685,8 +700,10 @@ aclremove(PG_FUNCTION_ARGS)
    old_aip = ACL_DAT(old_acl);
 
    /* Search for the matching entry */
-   for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
-       ;
+   for (dst = 0;
+        dst < old_num && !aclitem_match(mod_aip, old_aip + dst);
+        ++dst)
+       /* continue */ ;
 
    if (dst >= old_num)
    {
index 519ac8d1885a6e06de0cbfc36147107418771803..3aa70b0d3324c87975ec9a8bc311498d148d282e 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.3 2003/06/25 21:30:32 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.4 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
-
-/*-----------------------------------------------------------------------------
- * singleton_array :
- *     Form a multi-dimensional array given one starting element.
- *
- * - first argument is the datum with which to build the array
- * - second argument is the number of dimensions the array should have;
- *     defaults to 1 if no second argument is provided
- *----------------------------------------------------------------------------
- */
-Datum
-singleton_array(PG_FUNCTION_ARGS)
-{
-   Oid         elem_type = get_fn_expr_argtype(fcinfo, 0);
-   int         ndims;
-
-   if (elem_type == InvalidOid)
-       elog(ERROR, "Cannot determine input datatype");
-
-   if (PG_NARGS() == 2)
-       ndims = PG_GETARG_INT32(1);
-   else
-       ndims = 1;
-
-   PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
-                                                PG_GETARG_DATUM(0),
-                                                ndims));
-}
-
 /*-----------------------------------------------------------------------------
  * array_push :
  *     push an element onto either end of a one-dimensional array
@@ -70,6 +41,7 @@ array_push(PG_FUNCTION_ARGS)
    Oid         arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
    Oid         arg0_elemid;
    Oid         arg1_elemid;
+   ArrayMetaState *my_extra;
 
    if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
        elog(ERROR, "array_push: cannot determine input data types");
@@ -95,25 +67,54 @@ array_push(PG_FUNCTION_ARGS)
        PG_RETURN_NULL();       /* keep compiler quiet */
    }
 
-   /* Sanity check: do we have a one-dimensional array */
-   if (ARR_NDIM(v) != 1)
-       elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-   lb = ARR_LBOUND(v);
-   dimv = ARR_DIMS(v);
-   if (arg0_elemid != InvalidOid)
+   if (ARR_NDIM(v) == 1)
    {
-       /* append newelem */
-       int ub = dimv[0] + lb[0] - 1;
-       indx = ub + 1;
+       lb = ARR_LBOUND(v);
+       dimv = ARR_DIMS(v);
+
+       if (arg0_elemid != InvalidOid)
+       {
+           /* append newelem */
+           int ub = dimv[0] + lb[0] - 1;
+           indx = ub + 1;
+       }
+       else
+       {
+           /* prepend newelem */
+           indx = lb[0] - 1;
+       }
    }
+   else if (ARR_NDIM(v) == 0)
+       indx = 1;
    else
+       elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+   /*
+    * We arrange to look up info about element type only once per series
+    * of calls, assuming the element type doesn't change underneath us.
+    */
+   my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+   if (my_extra == NULL)
    {
-       /* prepend newelem */
-       indx = lb[0] - 1;
+       fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                    sizeof(ArrayMetaState));
+       my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+       my_extra->element_type = InvalidOid;
    }
 
-   get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+   if (my_extra->element_type != element_type)
+   {
+       /* Get info about element type */
+       get_typlenbyvalalign(element_type,
+                            &my_extra->typlen,
+                            &my_extra->typbyval,
+                            &my_extra->typalign);
+       my_extra->element_type = element_type;
+   }
+   typlen = my_extra->typlen;
+   typbyval = my_extra->typbyval;
+   typalign = my_extra->typalign;
 
    result = array_set(v, 1, &indx, newelem, -1,
                       typlen, typbyval, typalign, &isNull);
@@ -145,13 +146,28 @@ array_cat(PG_FUNCTION_ARGS)
 
    /*
     * We must have one of the following combinations of inputs:
-    * 1) two arrays with ndims1 == ndims2
-    * 2) ndims1 == ndims2 - 1
-    * 3) ndims1 == ndims2 + 1
+    * 1) one empty array, and one non-empty array
+    * 2) both arrays empty
+    * 3) two arrays with ndims1 == ndims2
+    * 4) ndims1 == ndims2 - 1
+    * 5) ndims1 == ndims2 + 1
     */
    ndims1 = ARR_NDIM(v1);
    ndims2 = ARR_NDIM(v2);
 
+   /*
+    * short circuit - if one input array is empty, and the other is not,
+    * we return the non-empty one as the result
+    *
+    * if both are empty, return the first one
+    */
+   if (ndims1 == 0 && ndims2 > 0)
+       PG_RETURN_ARRAYTYPE_P(v2);
+
+   if (ndims2 == 0)
+       PG_RETURN_ARRAYTYPE_P(v1);
+
+   /* the rest fall into combo 2, 3, or 4 */
    if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
        elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
                    "%d dimensions", ndims1, ndims2);
@@ -266,147 +282,15 @@ array_cat(PG_FUNCTION_ARGS)
    PG_RETURN_ARRAYTYPE_P(result);
 }
 
-/*----------------------------------------------------------------------------
- * array_accum :
- *     accumulator to build a 1-D array from input values -- this can be used
- *     to create custom aggregates.
- *
- * This function is not marked strict, so we have to be careful about nulls.
- *----------------------------------------------------------------------------
- */
-Datum
-array_accum(PG_FUNCTION_ARGS)
-{
-   /* return NULL if both arguments are NULL */
-   if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-       PG_RETURN_NULL();
-
-   /* create a new 1-D array from the new element if the array is NULL */
-   if (PG_ARGISNULL(0))
-   {
-       Oid         tgt_type = get_fn_expr_rettype(fcinfo);
-       Oid         tgt_elem_type;
-
-       if (tgt_type == InvalidOid)
-           elog(ERROR, "Cannot determine target array type");
-       tgt_elem_type = get_element_type(tgt_type);
-       if (tgt_elem_type == InvalidOid)
-           elog(ERROR, "Target type is not an array");
-
-       PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
-                                                    PG_GETARG_DATUM(1),
-                                                    1));
-   }
-
-   /* return the array if the new element is NULL */
-   if (PG_ARGISNULL(1))
-       PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
-   /*
-    * Otherwise this is equivalent to array_push.  We hack the call a little
-    * so that array_push can see the fn_expr information.
-    */
-   return array_push(fcinfo);
-}
-
-/*-----------------------------------------------------------------------------
- * array_assign :
- *     assign an element of an array to a new value and return the
- *     redefined array
- *----------------------------------------------------------------------------
- */
-Datum
-array_assign(PG_FUNCTION_ARGS)
-{
-   ArrayType  *v;
-   int         idx_to_chg;
-   Datum       newelem;
-   int        *dimv,
-              *lb, ub;
-   ArrayType  *result;
-   bool        isNull;
-   Oid         element_type;
-   int16       typlen;
-   bool        typbyval;
-   char        typalign;
-
-   v = PG_GETARG_ARRAYTYPE_P(0);
-   idx_to_chg = PG_GETARG_INT32(1);
-   newelem = PG_GETARG_DATUM(2);
-
-   /* Sanity check: do we have a one-dimensional array */
-   if (ARR_NDIM(v) != 1)
-       elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-   lb = ARR_LBOUND(v);
-   dimv = ARR_DIMS(v);
-   ub = dimv[0] + lb[0] - 1;
-   if (idx_to_chg < lb[0] || idx_to_chg > ub)
-       elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
-
-   element_type = ARR_ELEMTYPE(v);
-   /* Sanity check: do we have a non-zero element type */
-   if (element_type == 0)
-       elog(ERROR, "Invalid array element type: %u", element_type);
-
-   get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-   result = array_set(v, 1, &idx_to_chg, newelem, -1,
-                      typlen, typbyval, typalign, &isNull);
-
-   PG_RETURN_ARRAYTYPE_P(result);
-}
-
-/*-----------------------------------------------------------------------------
- * array_subscript :
- *     return specific element of an array
- *----------------------------------------------------------------------------
- */
-Datum
-array_subscript(PG_FUNCTION_ARGS)
-{
-   ArrayType  *v;
-   int         idx;
-   int        *dimv,
-              *lb, ub;
-   Datum       result;
-   bool        isNull;
-   Oid         element_type;
-   int16       typlen;
-   bool        typbyval;
-   char        typalign;
-
-   v = PG_GETARG_ARRAYTYPE_P(0);
-   idx = PG_GETARG_INT32(1);
-
-   /* Sanity check: do we have a one-dimensional array */
-   if (ARR_NDIM(v) != 1)
-       elog(ERROR, "Arrays greater than one-dimension are not supported");
-
-   lb = ARR_LBOUND(v);
-   dimv = ARR_DIMS(v);
-   ub = dimv[0] + lb[0] - 1;
-   if (idx < lb[0] || idx > ub)
-       elog(ERROR, "Cannot return nonexistent array element: %d", idx);
-
-   element_type = ARR_ELEMTYPE(v);
-   /* Sanity check: do we have a non-zero element type */
-   if (element_type == 0)
-       elog(ERROR, "Invalid array element type: %u", element_type);
-
-   get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
-   result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
-
-   PG_RETURN_DATUM(result);
-}
 
 /*
- * actually does the work for singleton_array(), and array_accum() if it is
- * given a null input array.
+ * used by text_to_array() in varlena.c
  */
 ArrayType *
-create_singleton_array(Oid element_type, Datum element, int ndims)
+create_singleton_array(FunctionCallInfo fcinfo,
+                      Oid element_type,
+                      Datum element,
+                      int ndims)
 {
    Datum   dvalues[1];
    int16   typlen;
@@ -415,6 +299,7 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
    int     dims[MAXDIM];
    int     lbs[MAXDIM];
    int     i;
+   ArrayMetaState *my_extra;
 
    if (element_type == 0)
        elog(ERROR, "Invalid array element type: %u", element_type);
@@ -429,7 +314,31 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
        lbs[i] = 1;
    }
 
-   get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+   /*
+    * We arrange to look up info about element type only once per series
+    * of calls, assuming the element type doesn't change underneath us.
+    */
+   my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+   if (my_extra == NULL)
+   {
+       fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                    sizeof(ArrayMetaState));
+       my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+       my_extra->element_type = InvalidOid;
+   }
+
+   if (my_extra->element_type != element_type)
+   {
+       /* Get info about element type */
+       get_typlenbyvalalign(element_type,
+                            &my_extra->typlen,
+                            &my_extra->typbyval,
+                            &my_extra->typalign);
+       my_extra->element_type = element_type;
+   }
+   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 c03d8c861d13fb64c82ae4d762ed9efc84361121..808353a63d4299344438d083c024f28ef72db0c0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.91 2003/06/25 21:30:32 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.92 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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"
 
 #define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)
 
-/* I/O function selector for system_cache_lookup */
-typedef enum IOFuncSelector
-{
-   IOFunc_input,
-   IOFunc_output,
-   IOFunc_receive,
-   IOFunc_send
-} IOFuncSelector;
-
-
 static int ArrayCount(char *str, int *dim, char typdelim);
 static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
             FmgrInfo *inputproc, Oid typelem, int32 typmod,
@@ -93,10 +85,6 @@ static Datum *ReadArrayBinary(StringInfo buf, int nitems,
 static void CopyArrayEls(char *p, Datum *values, int nitems,
             int typlen, bool typbyval, char typalign,
             bool freedata);
-static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
-                               int *typlen, bool *typbyval,
-                               char *typdelim, Oid *typelem,
-                               Oid *proc, char *typalign);
 static Datum ArrayCast(char *value, bool byval, int len);
 static int ArrayCastAndSet(Datum src,
                int typlen, bool typbyval, char typalign,
@@ -119,7 +107,7 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
                   char *destPtr,
                   int *st, int *endp, char *srcPtr,
                   int typlen, bool typbyval, char typalign);
-
+static int array_cmp(FunctionCallInfo fcinfo);
 
 /*---------------------------------------------------------------------
  * array_in :
@@ -139,12 +127,11 @@ array_in(PG_FUNCTION_ARGS)
                                                 * elements */
    int         typlen;
    bool        typbyval;
+   char        typalign;
    char        typdelim;
-   Oid         typinput;
    Oid         typelem;
    char       *string_save,
               *p;
-   FmgrInfo    inputproc;
    int         i,
                nitems;
    int32       nbytes;
@@ -153,13 +140,38 @@ array_in(PG_FUNCTION_ARGS)
    int         ndim,
                dim[MAXDIM],
                lBound[MAXDIM];
-   char        typalign;
+   ArrayMetaState *my_extra;
 
-   /* 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);
+   /*
+    * 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_io_data(element_type, IOFunc_input,
+                        &my_extra->typlen, &my_extra->typbyval,
+                        &my_extra->typalign, &my_extra->typdelim,
+                        &my_extra->typelem, &my_extra->typiofunc);
+       fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+                     fcinfo->flinfo->fn_mcxt);
+       my_extra->element_type = element_type;
+   }
+   typlen = my_extra->typlen;
+   typbyval = my_extra->typbyval;
+   typalign = my_extra->typalign;
+   typdelim = my_extra->typdelim;
+   typelem = my_extra->typelem;
 
    /* Make a modifiable copy of the input */
    /* XXX why are we allocating an extra 2 bytes here? */
@@ -262,7 +274,7 @@ array_in(PG_FUNCTION_ARGS)
    if (*p != '{')
        elog(ERROR, "array_in: missing left brace");
 
-   dataPtr = ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem,
+   dataPtr = ReadArrayStr(p, nitems, ndim, dim, &my_extra->proc, typelem,
                           typmod, typdelim, typlen, typbyval, typalign,
                           &nbytes);
    nbytes += ARR_OVERHEAD(ndim);
@@ -618,11 +630,9 @@ array_out(PG_FUNCTION_ARGS)
    Oid         element_type;
    int         typlen;
    bool        typbyval;
-   char        typdelim;
-   Oid         typoutput,
-               typelem;
-   FmgrInfo    outputproc;
    char        typalign;
+   char        typdelim;
+   Oid         typelem;
    char       *p,
               *tmp,
               *retval,
@@ -636,12 +646,40 @@ array_out(PG_FUNCTION_ARGS)
                indx[MAXDIM];
    int         ndim,
               *dim;
+   ArrayMetaState *my_extra;
 
    element_type = ARR_ELEMTYPE(v);
-   system_cache_lookup(element_type, IOFunc_output,
-                       &typlen, &typbyval, &typdelim,
-                       &typelem, &typoutput, &typalign);
-   fmgr_info(typoutput, &outputproc);
+
+   /*
+    * We arrange to look up info about element type, including its 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_io_data(element_type, IOFunc_output,
+                        &my_extra->typlen, &my_extra->typbyval,
+                        &my_extra->typalign, &my_extra->typdelim,
+                        &my_extra->typelem, &my_extra->typiofunc);
+       fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+                     fcinfo->flinfo->fn_mcxt);
+       my_extra->element_type = element_type;
+   }
+   typlen = my_extra->typlen;
+   typbyval = my_extra->typbyval;
+   typalign = my_extra->typalign;
+   typdelim = my_extra->typdelim;
+   typelem = my_extra->typelem;
 
    ndim = ARR_NDIM(v);
    dim = ARR_DIMS(v);
@@ -668,7 +706,7 @@ array_out(PG_FUNCTION_ARGS)
        bool        nq;
 
        itemvalue = fetch_att(p, typbyval, typlen);
-       values[i] = DatumGetCString(FunctionCall3(&outputproc,
+       values[i] = DatumGetCString(FunctionCall3(&my_extra->proc,
                                                  itemvalue,
                                               ObjectIdGetDatum(typelem),
                                                  Int32GetDatum(-1)));
@@ -786,10 +824,8 @@ array_recv(PG_FUNCTION_ARGS)
    Oid         element_type;
    int         typlen;
    bool        typbyval;
-   char        typdelim;
-   Oid         typreceive;
+   char        typalign;
    Oid         typelem;
-   FmgrInfo    receiveproc;
    int         i,
                nitems;
    int32       nbytes;
@@ -799,7 +835,7 @@ array_recv(PG_FUNCTION_ARGS)
                flags,
                dim[MAXDIM],
                lBound[MAXDIM];
-   char        typalign;
+   ArrayMetaState *my_extra;
 
    /* Get the array header information */
    ndim = pq_getmsgint(buf, 4);
@@ -831,16 +867,40 @@ array_recv(PG_FUNCTION_ARGS)
        PG_RETURN_ARRAYTYPE_P(retval);
    }
 
-   /* Get info about element type, including its receive conversion proc */
-   system_cache_lookup(element_type, IOFunc_receive,
-                       &typlen, &typbyval, &typdelim,
-                       &typelem, &typreceive, &typalign);
-   if (!OidIsValid(typreceive))
-       elog(ERROR, "No binary input function available for type %s",
-            format_type_be(element_type));
-   fmgr_info(typreceive, &receiveproc);
+   /*
+    * We arrange to look up info about element type, including its receive
+    * conversion proc, only once per series of calls, assuming the element
+    * type doesn't change underneath us.
+    */
+   my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+   if (my_extra == NULL)
+   {
+       fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                    sizeof(ArrayMetaState));
+       my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+       my_extra->element_type = InvalidOid;
+   }
+
+   if (my_extra->element_type != element_type)
+   {
+       /* Get info about element type, including its receive proc */
+       get_type_io_data(element_type, IOFunc_receive,
+                        &my_extra->typlen, &my_extra->typbyval,
+                        &my_extra->typalign, &my_extra->typdelim,
+                        &my_extra->typelem, &my_extra->typiofunc);
+       if (!OidIsValid(my_extra->typiofunc))
+           elog(ERROR, "No binary input function available for type %s",
+                format_type_be(element_type));
+       fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+                     fcinfo->flinfo->fn_mcxt);
+       my_extra->element_type = element_type;
+   }
+   typlen = my_extra->typlen;
+   typbyval = my_extra->typbyval;
+   typalign = my_extra->typalign;
+   typelem = my_extra->typelem;
 
-   dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
+   dataPtr = ReadArrayBinary(buf, nitems, &my_extra->proc, typelem,
                              typlen, typbyval, typalign,
                              &nbytes);
    nbytes += ARR_OVERHEAD(ndim);
@@ -965,26 +1025,51 @@ array_send(PG_FUNCTION_ARGS)
    Oid         element_type;
    int         typlen;
    bool        typbyval;
-   char        typdelim;
-   Oid         typsend,
-               typelem;
-   FmgrInfo    sendproc;
    char        typalign;
+   Oid         typelem;
    char       *p;
    int         nitems,
                i;
    int         ndim,
               *dim;
    StringInfoData buf;
+   ArrayMetaState *my_extra;
 
    /* Get information about the element type and the array dimensions */
    element_type = ARR_ELEMTYPE(v);
-   system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
-                       &typdelim, &typelem, &typsend, &typalign);
-   if (!OidIsValid(typsend))
-       elog(ERROR, "No binary output function available for type %s",
-            format_type_be(element_type));
-   fmgr_info(typsend, &sendproc);
+
+   /*
+    * We arrange to look up info about element type, including its send
+    * 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 send proc */
+       get_type_io_data(element_type, IOFunc_send,
+                        &my_extra->typlen, &my_extra->typbyval,
+                        &my_extra->typalign, &my_extra->typdelim,
+                        &my_extra->typelem, &my_extra->typiofunc);
+       if (!OidIsValid(my_extra->typiofunc))
+           elog(ERROR, "No binary output function available for type %s",
+                format_type_be(element_type));
+       fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+                     fcinfo->flinfo->fn_mcxt);
+       my_extra->element_type = element_type;
+   }
+   typlen = my_extra->typlen;
+   typbyval = my_extra->typbyval;
+   typalign = my_extra->typalign;
+   typelem = my_extra->typelem;
 
    ndim = ARR_NDIM(v);
    dim = ARR_DIMS(v);
@@ -1011,7 +1096,7 @@ array_send(PG_FUNCTION_ARGS)
 
        itemvalue = fetch_att(p, typbyval, typlen);
 
-       outputbytes = DatumGetByteaP(FunctionCall2(&sendproc,
+       outputbytes = DatumGetByteaP(FunctionCall2(&my_extra->proc,
                                                   itemvalue,
                                                   ObjectIdGetDatum(typelem)));
        /* We assume the result will not have been toasted */
@@ -1476,6 +1561,26 @@ array_set(ArrayType *array,
    array = DatumGetArrayTypeP(PointerGetDatum(array));
 
    ndim = ARR_NDIM(array);
+
+   /*
+    * if number of dims is zero, i.e. an empty array, create an array
+    * with nSubscripts dimensions, and set the lower bounds to the supplied
+    * subscripts
+    */
+   if (ndim == 0)
+   {
+       Oid     elmtype = ARR_ELEMTYPE(array);
+
+       for (i = 0; i < nSubscripts; i++)
+       {
+           dim[i] = 1;
+           lb[i] = indx[i];
+       }
+
+       return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+                                               elmlen, elmbyval, elmalign);
+   }
+
    if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
        elog(ERROR, "Invalid array subscripts");
 
@@ -1632,6 +1737,31 @@ array_set_slice(ArrayType *array,
    /* note: we assume srcArray contains no toasted elements */
 
    ndim = ARR_NDIM(array);
+
+   /*
+    * if number of dims is zero, i.e. an empty array, create an array
+    * with nSubscripts dimensions, and set the upper and lower bounds
+    * to the supplied subscripts
+    */
+   if (ndim == 0)
+   {
+       Datum  *dvalues;
+       int     nelems;
+       Oid     elmtype = ARR_ELEMTYPE(array);
+
+       deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+                                                       &dvalues, &nelems);
+
+       for (i = 0; i < nSubscripts; i++)
+       {
+           dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+           lb[i] = lowerIndx[i];
+       }
+
+       return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+                                                elmlen, elmbyval, elmalign);
+   }
+
    if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
        elog(ERROR, "Invalid array subscripts");
 
@@ -1807,10 +1937,14 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
    int         typlen;
    bool        typbyval;
    char        typalign;
-   char        typdelim;
-   Oid         typelem;
-   Oid         proc;
    char       *s;
+   typedef struct {
+       ArrayMetaState inp_extra;
+       ArrayMetaState ret_extra;
+   } am_extra;
+   am_extra  *my_extra;
+   ArrayMetaState *inp_extra;
+   ArrayMetaState *ret_extra;
 
    /* Get input array */
    if (fcinfo->nargs < 1)
@@ -1829,11 +1963,51 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
    if (nitems <= 0)
        PG_RETURN_ARRAYTYPE_P(v);
 
-   /* Lookup source and result types. Unneeded variables are reused. */
-   system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
-                       &typdelim, &typelem, &proc, &inp_typalign);
-   system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
-                       &typdelim, &typelem, &proc, &typalign);
+   /*
+    * We arrange to look up info about input and return element types only
+    * once per series of calls, assuming the element type doesn't change
+    * underneath us.
+    */
+   my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
+   if (my_extra == NULL)
+   {
+       fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                     sizeof(am_extra));
+       my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
+       inp_extra = &my_extra->inp_extra;
+       inp_extra->element_type = InvalidOid;
+       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)
+   {
+       get_typlenbyvalalign(inpType,
+                            &inp_extra->typlen,
+                            &inp_extra->typbyval,
+                            &inp_extra->typalign);
+       inp_extra->element_type = inpType;
+   }
+   inp_typlen = inp_extra->typlen;
+   inp_typbyval = inp_extra->typbyval;
+   inp_typalign = inp_extra->typalign;
+
+   if (ret_extra->element_type != retType)
+   {
+       get_typlenbyvalalign(retType,
+                            &ret_extra->typlen,
+                            &ret_extra->typbyval,
+                            &ret_extra->typalign);
+       ret_extra->element_type = retType;
+   }
+   typlen = ret_extra->typlen;
+   typbyval = ret_extra->typbyval;
+   typalign = ret_extra->typalign;
 
    /* Allocate temporary array for new values */
    values = (Datum *) palloc(nitems * sizeof(Datum));
@@ -2049,8 +2223,6 @@ deconstruct_array(ArrayType *array,
  *       compares two arrays for equality
  * result :
  *       returns true if the arrays are equal, false otherwise.
- *
- * XXX bitwise equality is pretty bogus ...
  *-----------------------------------------------------------------------------
  */
 Datum
@@ -2058,12 +2230,101 @@ array_eq(PG_FUNCTION_ARGS)
 {
    ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
    ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+   char       *p1 = (char *) ARR_DATA_PTR(array1);
+   char       *p2 = (char *) ARR_DATA_PTR(array2);
+   int         ndims1 = ARR_NDIM(array1);
+   int         ndims2 = ARR_NDIM(array2);
+   int        *dims1 = ARR_DIMS(array1);
+   int        *dims2 = ARR_DIMS(array2);
+   int         nitems1 = ArrayGetNItems(ndims1, dims1);
+   int         nitems2 = ArrayGetNItems(ndims2, dims2);
+   Oid         element_type = ARR_ELEMTYPE(array1);
+   FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
    bool        result = true;
+   int         typlen;
+   bool        typbyval;
+   char        typalign;
+   int         i;
+   ArrayMetaState *my_extra;
+   FunctionCallInfoData locfcinfo;
 
-   if (ARR_SIZE(array1) != ARR_SIZE(array2))
-       result = false;
-   else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
+   if (element_type != ARR_ELEMTYPE(array2))
+       elog(ERROR, "cannot compare arrays of different element types");
+
+   /* 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);
+
+           get_typlenbyvalalign(element_type,
+                                &my_extra->typlen,
+                                &my_extra->typbyval,
+                                &my_extra->typalign);
+           fmgr_info_cxt(opfuncid, &my_extra->proc,
+                         ae_fmgr_info->fn_mcxt);
+           my_extra->element_type = element_type;
+       }
+       typlen = my_extra->typlen;
+       typbyval = my_extra->typbyval;
+       typalign = my_extra->typalign;
+
+       /*
+        * apply the operator to each pair of array elements.
+        */
+       MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+       locfcinfo.flinfo = &my_extra->proc;
+       locfcinfo.nargs = 2;
+
+       /* Loop over source data */
+       for (i = 0; i < nitems1; i++)
+       {
+           Datum   elt1;
+           Datum   elt2;
+           bool    oprresult;
+
+           /* Get element pair */
+           elt1 = fetch_att(p1, typbyval, typlen);
+           elt2 = fetch_att(p2, typbyval, typlen);
+
+           p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+           p1 = (char *) att_align(p1, typalign);
+
+           p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+           p2 = (char *) att_align(p2, typalign);
+
+           /*
+            * Apply the operator to the element pair
+            */
+           locfcinfo.arg[0] = elt1;
+           locfcinfo.arg[1] = elt2;
+           locfcinfo.argnull[0] = false;
+           locfcinfo.argnull[1] = false;
+           locfcinfo.isnull = false;
+           oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+           if (!oprresult)
+           {
+               result = false;
+               break;
+           }
+       }
+   }
 
    /* Avoid leaking memory when handed toasted input. */
    PG_FREE_IF_COPY(array1, 0);
@@ -2073,53 +2334,171 @@ array_eq(PG_FUNCTION_ARGS)
 }
 
 
-/***************************************************************************/
-/******************|         Support  Routines           |*****************/
-/***************************************************************************/
+/*-----------------------------------------------------------------------------
+ * array-array bool operators:
+ *     Given two arrays, iterate comparison operators
+ *     over the array. Uses logic similar to text comparison
+ *     functions, except element-by-element instead of
+ *     character-by-character.
+ *----------------------------------------------------------------------------
+ */
+Datum
+array_ne(PG_FUNCTION_ARGS)
+{
+   PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
+}
 
-static void
-system_cache_lookup(Oid element_type,
-                   IOFuncSelector which_func,
-                   int *typlen,
-                   bool *typbyval,
-                   char *typdelim,
-                   Oid *typelem,
-                   Oid *proc,
-                   char *typalign)
+Datum
+array_lt(PG_FUNCTION_ARGS)
+{
+   PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
+}
+
+Datum
+array_gt(PG_FUNCTION_ARGS)
+{
+   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)
 {
-   HeapTuple   typeTuple;
-   Form_pg_type typeStruct;
-
-   typeTuple = SearchSysCache(TYPEOID,
-                              ObjectIdGetDatum(element_type),
-                              0, 0, 0);
-   if (!HeapTupleIsValid(typeTuple))
-       elog(ERROR, "cache lookup failed for type %u", element_type);
-   typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
-   *typlen = typeStruct->typlen;
-   *typbyval = typeStruct->typbyval;
-   *typdelim = typeStruct->typdelim;
-   *typelem = typeStruct->typelem;
-   *typalign = typeStruct->typalign;
-   switch (which_func)
-   {
-       case IOFunc_input:
-           *proc = typeStruct->typinput;
-           break;
-       case IOFunc_output:
-           *proc = typeStruct->typoutput;
-           break;
-       case IOFunc_receive:
-           *proc = typeStruct->typreceive;
-           break;
-       case IOFunc_send:
-           *proc = typeStruct->typsend;
-           break;
-   }
-   ReleaseSysCache(typeTuple);
+   PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
+}
+
+Datum
+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        typalign;
+   Datum      *dvalues1;
+   int         nelems1;
+   Datum      *dvalues2;
+   int         nelems2;
+   int         min_nelems;
+   int         i;
+   typedef struct
+   {
+       Oid             element_type;
+       int16           typlen;
+       bool            typbyval;
+       char            typalign;
+       FmgrInfo        eqproc;
+       FmgrInfo        ordproc;
+   } ac_extra;
+   ac_extra *my_extra;
+
+   element_type = ARR_ELEMTYPE(array1);
+   if (element_type != ARR_ELEMTYPE(array2))
+       elog(ERROR, "cannot compare arrays of different element types");
+
+   /*
+    * We arrange to look up the element type info and related functions
+    * only once per series of calls, assuming the element type doesn'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);
+
+       get_typlenbyvalalign(element_type,
+                            &my_extra->typlen,
+                            &my_extra->typbyval,
+                            &my_extra->typalign);
+       fmgr_info_cxt(eqfuncid, &my_extra->eqproc,
+                     ac_fmgr_info->fn_mcxt);
+       fmgr_info_cxt(ordfuncid, &my_extra->ordproc,
+                     ac_fmgr_info->fn_mcxt);
+       my_extra->element_type = element_type;
+   }
+   typlen = my_extra->typlen;
+   typbyval = my_extra->typbyval;
+   typalign = my_extra->typalign;
+
+   /* extract a C array of arg array datums */
+   deconstruct_array(array1, element_type, typlen, typbyval, typalign,
+                                                   &dvalues1, &nelems1);
+
+   deconstruct_array(array2, element_type, typlen, typbyval, typalign,
+                                                   &dvalues2, &nelems2);
+
+   min_nelems = Min(nelems1, nelems2);
+   for (i = 0; i < min_nelems; i++)
+   {
+       /* are they equal */
+       opresult = FunctionCall2(&my_extra->eqproc,
+                                dvalues1[i], dvalues2[i]);
+
+       if (!DatumGetBool(opresult))
+       {
+           /* nope, see if arg1 is less than arg2 */
+           opresult = FunctionCall2(&my_extra->ordproc,
+                                    dvalues1[i], dvalues2[i]);
+           if (DatumGetBool(opresult))
+           {
+               /* arg1 is less than arg2 */
+               result = -1;
+               break;
+           }
+           else
+           {
+               /* arg1 is greater than arg2 */
+               result = 1;
+               break;
+           }
+       }
+   }
+
+   if ((result == 0) && (nelems1 != nelems2))
+       result = (nelems1 < nelems2) ? -1 : 1;
+
+   /* Avoid leaking memory when handed toasted input. */
+   PG_FREE_IF_COPY(array1, 0);
+   PG_FREE_IF_COPY(array2, 1);
+
+   return result;
+}
+
+
+/***************************************************************************/
+/******************|         Support  Routines           |*****************/
+/***************************************************************************/
+
 /*
  * Fetch array element at pointer, converted correctly to a Datum
  */
@@ -2423,6 +2802,18 @@ array_type_coerce(PG_FUNCTION_ARGS)
        if (tgt_elem_type == InvalidOid)
            elog(ERROR, "Target type is not an array");
 
+       /*
+        * We don't deal with domain constraints yet, so bail out.
+        * This isn't currently a problem, because we also don't
+        * support arrays of domain type elements either. But in the
+        * future we might. At that point consideration should be given
+        * to removing the check below and adding a domain constraints
+        * check to the coercion.
+        */
+       if (getBaseType(tgt_elem_type) != tgt_elem_type)
+           elog(ERROR, "array coercion to domain type elements not " \
+                       "currently supported");
+
        if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
                                   COERCION_EXPLICIT, &funcId))
        {
@@ -2439,10 +2830,16 @@ array_type_coerce(PG_FUNCTION_ARGS)
    }
 
    /*
-    * If it's binary-compatible, return the array unmodified.
+    * If it's binary-compatible, modify the element type in the array header,
+    * but otherwise leave the array as we received it.
     */
    if (my_extra->coerce_finfo.fn_oid == InvalidOid)
-       PG_RETURN_ARRAYTYPE_P(src);
+   {
+       ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
+       
+       ARR_ELEMTYPE(result) = my_extra->desttype;
+       PG_RETURN_ARRAYTYPE_P(result);
+   }
 
    /*
     * Use array_map to apply the function to each array element.
@@ -2454,3 +2851,121 @@ array_type_coerce(PG_FUNCTION_ARGS)
 
    return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
 }
+
+/*
+ * accumArrayResult - accumulate one (more) Datum for an array result
+ *
+ * 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 1-D 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 multi-D final result of accumArrayResult
+ *
+ * beware: no check that specified dimensions match the number of values
+ * accumulated.
+ *
+ * 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 ce6443647e2240c5df6cd666c4e2781796a4b718..37413bcd59f799a25ab6b3389ac6e4919f2fb47b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.100 2003/06/25 21:30:32 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.101 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "access/tuptoaster.h"
+#include "catalog/pg_type.h"
 #include "lib/stringinfo.h"
 #include "libpq/crypt.h"
 #include "libpq/pqformat.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/pg_locale.h"
+#include "utils/lsyscache.h"
 
 
 typedef struct varlena unknown;
@@ -1983,8 +1986,7 @@ split_text(PG_FUNCTION_ARGS)
        if (fldnum == 1)        /* first field - just return the input
                                 * string */
            PG_RETURN_TEXT_P(inputstring);
-       else
-/* otherwise return an empty string */
+       else                    /* otherwise return an empty string */
            PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
    }
 
@@ -2004,8 +2006,7 @@ split_text(PG_FUNCTION_ARGS)
        if (fldnum == 1)        /* first field - just return the input
                                 * string */
            PG_RETURN_TEXT_P(inputstring);
-       else
-/* otherwise return an empty string */
+       else                    /* otherwise return an empty string */
            PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
    }
    else if ((start_posn != 0) && (end_posn == 0))
@@ -2028,6 +2029,176 @@ split_text(PG_FUNCTION_ARGS)
    }
 }
 
+/*
+ * text_to_array
+ * parse input string
+ * return text array of elements
+ * based on provided field separator
+ */
+Datum
+text_to_array(PG_FUNCTION_ARGS)
+{
+   text       *inputstring = PG_GETARG_TEXT_P(0);
+   int         inputstring_len = TEXTLEN(inputstring);
+   text       *fldsep = PG_GETARG_TEXT_P(1);
+   int         fldsep_len = TEXTLEN(fldsep);
+   int         fldnum;
+   int         start_posn = 0;
+   int         end_posn = 0;
+   text       *result_text = NULL;
+   ArrayBuildState *astate = NULL;
+   MemoryContext oldcontext = CurrentMemoryContext;
+
+   /* return NULL for empty input string */
+   if (inputstring_len < 1)
+       PG_RETURN_NULL();
+
+   /* empty field separator
+    * return one element, 1D, array using the input string */
+   if (fldsep_len < 1)
+       PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                             CStringGetDatum(inputstring), 1));
+
+   /* start with end position holding the initial start position */
+   end_posn = 0;
+   for (fldnum=1;;fldnum++)    /* field number is 1 based */
+   {
+       Datum   dvalue;
+       bool    disnull = false;
+
+       start_posn = end_posn;
+       end_posn = text_position(PointerGetDatum(inputstring),
+                                PointerGetDatum(fldsep),
+                                fldnum);
+
+       if ((start_posn == 0) && (end_posn == 0))   /* fldsep not found */
+       {
+           if (fldnum == 1)
+           {
+               /* first element
+                * return one element, 1D, array using the input string */
+               PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+                                     CStringGetDatum(inputstring), 1));
+           }
+           else
+           {
+               /* otherwise create array and exit */
+               PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+           }
+       }
+       else if ((start_posn != 0) && (end_posn == 0))
+       {
+           /* last field requested */
+           result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+       }
+       else if ((start_posn == 0) && (end_posn != 0))
+       {
+           /* first field requested */
+           result_text = LEFT(inputstring, fldsep);
+       }
+       else
+       {
+           /* prior to last field requested */
+           result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false);
+       }
+
+       /* stash away current value */
+       dvalue = PointerGetDatum(result_text);
+       astate = accumArrayResult(astate, dvalue,
+                                 disnull, TEXTOID, oldcontext);
+
+   }
+
+   /* never reached -- keep compiler quiet */
+   PG_RETURN_NULL();
+}
+
+/*
+ * array_to_text
+ * concatenate Cstring representation of input array elements
+ * using provided field separator
+ */
+Datum
+array_to_text(PG_FUNCTION_ARGS)
+{
+   ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+   char       *fldsep = PG_TEXTARG_GET_STR(1);
+   int         nitems, *dims, ndims;
+   char       *p;
+   Oid         element_type;
+   int         typlen;
+   bool        typbyval;
+   char        typalign;
+   Oid         typelem;
+   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_io_data(element_type, IOFunc_output,
+                        &my_extra->typlen, &my_extra->typbyval,
+                        &my_extra->typalign, &my_extra->typdelim,
+                        &my_extra->typelem, &my_extra->typiofunc);
+       fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+                     fcinfo->flinfo->fn_mcxt);
+       my_extra->element_type = element_type;
+   }
+   typlen = my_extra->typlen;
+   typbyval = my_extra->typbyval;
+   typalign = my_extra->typalign;
+   typelem = my_extra->typelem;
+
+   for (i = 0; i < nitems; i++)
+   {
+       Datum       itemvalue;
+       char       *value;
+
+       itemvalue = fetch_att(p, typbyval, typlen);
+
+       value = DatumGetCString(FunctionCall3(&my_extra->proc,
+                                             itemvalue,
+                                             ObjectIdGetDatum(typelem),
+                                             Int32GetDatum(-1)));
+
+       if (i > 0)
+           appendStringInfo(result_str, "%s%s", fldsep, value);
+       else
+           appendStringInfo(result_str, "%s", value);
+
+       p = att_addlength(p, typlen, PointerGetDatum(p));
+       p = (char *) att_align(p, typalign);
+   }
+
+   PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
+}
+
 #define HEXBASE 16
 /*
  * Convert a int32 to a string containing a base 16 (hex) representation of
index 479f23090fb7ecd20896ef1e60343b20d243b214..6da53870c47a8a3c676c1295b620d99b40768da2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.99 2003/06/25 21:30:32 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.100 2003/06/27 00:33:25 tgl Exp $
  *
  * NOTES
  *   Eventually, the index information should go through here, too.
@@ -1090,6 +1090,56 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
    ReleaseSysCache(tp);
 }
 
+/*
+ * get_type_io_data
+ *
+ *     A six-fer:  given the type OID, return typlen, typbyval, typalign,
+ *                 typdelim, typelem, and IO function OID. The IO function
+ *                 returned is controlled by IOFuncSelector
+ */
+void
+get_type_io_data(Oid typid,
+                IOFuncSelector which_func,
+                int16 *typlen,
+                bool *typbyval,
+                char *typalign,
+                char *typdelim,
+                Oid *typelem,
+                Oid *func)
+{
+   HeapTuple   typeTuple;
+   Form_pg_type typeStruct;
+
+   typeTuple = SearchSysCache(TYPEOID,
+                              ObjectIdGetDatum(typid),
+                              0, 0, 0);
+   if (!HeapTupleIsValid(typeTuple))
+       elog(ERROR, "cache lookup failed for type %u", typid);
+   typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+   *typlen = typeStruct->typlen;
+   *typbyval = typeStruct->typbyval;
+   *typalign = typeStruct->typalign;
+   *typdelim = typeStruct->typdelim;
+   *typelem = typeStruct->typelem;
+   switch (which_func)
+   {
+       case IOFunc_input:
+           *func = typeStruct->typinput;
+           break;
+       case IOFunc_output:
+           *func = typeStruct->typoutput;
+           break;
+       case IOFunc_receive:
+           *func = typeStruct->typreceive;
+           break;
+       case IOFunc_send:
+           *func = typeStruct->typsend;
+           break;
+   }
+   ReleaseSysCache(typeTuple);
+}
+
 #ifdef NOT_USED
 char
 get_typalign(Oid typid)
index 8bba8c953dbd9e9cd6dc9f32db5a4baa7a5407b0..e8ce5d131a5b6e9b6d374b9d120247bd18294b02 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.200 2003/06/25 01:26:16 momjian Exp $
+ * $Id: catversion.h,v 1.201 2003/06/27 00:33:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200306241
+#define CATALOG_VERSION_NO 200306261
 
 #endif
index 4b6107ce53e89c86f862e78afca2732ab29b56a3..e2816b3c214426901b7a86c26b0fd291b81e4a98 100644 (file)
@@ -16,7 +16,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amop.h,v 1.52 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_amop.h,v 1.53 2003/06/27 00:33:25 tgl Exp $
  *
  * NOTES
  *  the genbki.sh script reads this file and generates .bki
@@ -418,6 +418,15 @@ DATA(insert (  2098 3 f 2334 ));
 DATA(insert (  2098 4 f 2335 ));
 DATA(insert (  2098 5 f 2336 ));
 
+/*
+ * btree array_ops
+ */
+
+DATA(insert (   397 1 f 1072 ));
+DATA(insert (   397 2 f 1074 ));
+DATA(insert (   397 3 f 1070 ));
+DATA(insert (   397 4 f 1075 ));
+DATA(insert (   397 5 f 1073 ));
 
 /*
  * hash index _ops
index 6febdecffcd62d6b7510c6708687f8abbf047ff9..79f181cd6952a5d7562c14a57c37c58c4d99c5dc 100644 (file)
@@ -14,7 +14,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amproc.h,v 1.40 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_amproc.h,v 1.41 2003/06/27 00:33:25 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -78,6 +78,7 @@ DATA(insert ( 1993 3  199 ));
 
 
 /* btree */
+DATA(insert (   397 1  382 ));
 DATA(insert (   421 1  357 ));
 DATA(insert (   423 1 1596 ));
 DATA(insert (   424 1 1693 ));
index 88d2aac1a7da4abfdc57d55a41d01bc63c82961e..5d635523ca9ee31872b665cbe7ec2598bc6b3057 100644 (file)
@@ -26,7 +26,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_opclass.h,v 1.53 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_opclass.h,v 1.54 2003/06/27 00:33:25 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -87,6 +87,8 @@ typedef FormData_pg_opclass *Form_pg_opclass;
  */
 
 DATA(insert OID =  421 (   403     abstime_ops     PGNSP PGUID  702 t 0 ));
+DATA(insert OID =  397 (   403     array_ops       PGNSP PGUID 2277 t 0 ));
+#define ARRAY_BTREE_OPS_OID 397
 DATA(insert OID =  422 (   402     bigbox_ops      PGNSP PGUID  603 f 0 ));
 DATA(insert OID =  423 (   403     bit_ops         PGNSP PGUID 1560 t 0 ));
 DATA(insert OID =  424 (   403     bool_ops        PGNSP PGUID   16 t 0 ));
index 2bbef276c00f938cc143a53647e6d0b14f3c57ae..a964ac1b3dd9b7f4223ab4aea060f0cfeb97c807 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.117 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_operator.h,v 1.118 2003/06/27 00:33:25 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -116,7 +116,6 @@ DATA(insert OID =  96 ( "="        PGNSP PGUID b t  23  23  16  96 518  97  97  97 521 int
 DATA(insert OID =  97 ( "<"           PGNSP PGUID b f  23  23  16 521 525   0   0   0   0 int4lt scalarltsel scalarltjoinsel ));
 DATA(insert OID =  98 ( "="           PGNSP PGUID b t  25  25  16  98 531 664 664 664 666 texteq eqsel eqjoinsel ));
 
-DATA(insert OID = 329 (  "="      PGNSP PGUID b f 2277 2277 16   329 0 0 0 0 0 array_eq     eqsel eqjoinsel ));
 DATA(insert OID = 349 (  "||"     PGNSP PGUID b f 2277 2283 2277   0 0 0 0 0 0 array_append   -       -     ));
 DATA(insert OID = 374 (  "||"     PGNSP PGUID b f 2283 2277 2277   0 0 0 0 0 0 array_prepend  -       -     ));
 DATA(insert OID = 375 (  "||"     PGNSP PGUID b f 2277 2277 2277   0 0 0 0 0 0 array_cat      -       -     ));
@@ -425,6 +424,7 @@ DATA(insert OID = 965 (  "^"       PGNSP PGUID b f  701  701    701 0 0 0 0 0 0 dpow -
 DATA(insert OID = 966 (  "+"      PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
 DATA(insert OID = 967 (  "-"      PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
 DATA(insert OID = 968 (  "~"      PGNSP PGUID b f 1034 1033     16 0 0 0 0 0 0 aclcontains - - ));
+DATA(insert OID = 974 (  "="      PGNSP PGUID b f 1033 1033     16 974 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 - - ));
@@ -441,6 +441,16 @@ DATA(insert OID = 1059 ( "<="     PGNSP PGUID b f 1042 1042     16 1061 1060  0 0 0
 DATA(insert OID = 1060 ( ">"      PGNSP PGUID b f 1042 1042     16 1058 1059  0 0 0 0 bpchargt scalargtsel scalargtjoinsel ));
 DATA(insert OID = 1061 ( ">="     PGNSP PGUID b f 1042 1042     16 1059 1058  0 0 0 0 bpcharge scalargtsel scalargtjoinsel ));
 
+/* generic array comparison operators */
+DATA(insert OID = 1070 (  "="     PGNSP PGUID b f 2277 2277 16 1070 1071  1072 1072 1072 1073 array_eq eqsel eqjoinsel ));
+#define ARRAY_EQ_OP    1070
+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 ));
+#define ARRAY_LT_OP    1072
+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 ));
+
 /* date operators */
 DATA(insert OID = 1076 ( "+"      PGNSP PGUID b f  1082    1186 1114 0 0 0 0 0 0 date_pl_interval - - ));
 DATA(insert OID = 1077 ( "-"      PGNSP PGUID b f  1082    1186 1114 0 0 0 0 0 0 date_mi_interval - - ));
index bae6f644fc577cb6163b27fb37784227420c1eaf..578a6b7d42a639f02263cf3dc45096b99de9d055 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.307 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_proc.h,v 1.308 2003/06/27 00:33:25 tgl Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -758,6 +758,8 @@ DATA(insert OID = 359 (  btnamecmp         PGNSP PGUID 12 f f t f i 2 23 "19 19"    btn
 DESCR("btree less-equal-greater");
 DATA(insert OID = 360 (  bttextcmp        PGNSP PGUID 12 f f t f i 2 23 "25 25"    bttextcmp - _null_ ));
 DESCR("btree less-equal-greater");
+DATA(insert OID = 382 (  btarraycmp           PGNSP PGUID 12 f f t f i 2 23 "2277 2277"    btarraycmp - _null_ ));
+DESCR("btree less-equal-greater");
 
 DATA(insert OID = 361 (  lseg_distance    PGNSP PGUID 12 f f t f i 2 701 "601 601"  lseg_distance - _null_ ));
 DESCR("distance between");
@@ -988,14 +990,23 @@ DESCR("greater-than");
 DATA(insert OID = 743 (  text_ge          PGNSP PGUID 12 f f t f i 2 16 "25 25"    text_ge - _null_ ));
 DESCR("greater-than-or-equal");
 
-DATA(insert OID = 744 (  array_eq         PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
-DESCR("array equal");
-
 DATA(insert OID = 745 (  current_user     PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
 DESCR("current user name");
 DATA(insert OID = 746 (  session_user     PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
 DESCR("session user name");
 
+DATA(insert OID = 744 (  array_eq         PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
+DESCR("array equal");
+DATA(insert OID = 390 (  array_ne         PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
+DESCR("array not equal");
+DATA(insert OID = 391 (  array_lt         PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
+DESCR("array less than");
+DATA(insert OID = 392 (  array_gt         PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
+DESCR("array greater than");
+DATA(insert OID = 393 (  array_le         PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
+DESCR("array less than or equal");
+DATA(insert OID = 396 (  array_ge         PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
+DESCR("array greater than or equal");
 DATA(insert OID = 747 (  array_dims           PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
 DESCR("array dimensions");
 DATA(insert OID = 750 (  array_in         PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23"  array_in - _null_ ));
@@ -1006,22 +1017,18 @@ DATA(insert OID = 2091 (  array_lower      PGNSP PGUID 12 f f t f i 2 23 "2277 23"
 DESCR("array lower dimension");
 DATA(insert OID = 2092 (  array_upper     PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
 DESCR("array upper dimension");
-DATA(insert OID = 377 (  singleton_array  PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
-DESCR("create array from single element");
 DATA(insert OID = 378 (  array_append     PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
 DESCR("append element onto end of array");
 DATA(insert OID = 379 (  array_prepend    PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
 DESCR("prepend element onto front of array");
-DATA(insert OID = 380 (  array_accum      PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
-DESCR("push element onto end of array, creating array if needed");
-DATA(insert OID = 381 (  array_assign     PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
-DESCR("assign specific array element");
-DATA(insert OID = 382 (  array_subscript  PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
-DESCR("return specific array element");
 DATA(insert OID = 383 (  array_cat        PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
 DESCR("concatenate two arrays");
 DATA(insert OID = 384  (  array_coerce    PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
 DESCR("coerce array type to another array type");
+DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+DESCR("split delimited text into text[]");
+DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+DESCR("concatenate array elements, using delimiter, into text");
 
 DATA(insert OID = 760 (  smgrin               PGNSP PGUID 12 f f t f s 1 210 "2275"  smgrin - _null_ ));
 DESCR("I/O");
@@ -1322,6 +1329,8 @@ DATA(insert OID = 1036 (  aclremove          PGNSP PGUID 12 f f t f s 2 1034 "1034 10
 DESCR("remove ACL item");
 DATA(insert OID = 1037 (  aclcontains     PGNSP PGUID 12 f f t f s 2 16 "1034 1033"    aclcontains - _null_ ));
 DESCR("does ACL contain item?");
+DATA(insert OID = 1062 (  aclitemeq           PGNSP PGUID 12 f f t f s 2 16 "1033 1033"    aclitem_eq - _null_ ));
+DESCR("equality operator for ACL items");
 DATA(insert OID = 1365 (  makeaclitem     PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16" makeaclitem - _null_ ));
 DESCR("make ACL item");
 DATA(insert OID = 1038 (  seteval         PGNSP PGUID 12 f f t t v 1 23 "26"  seteval - _null_ ));
index 13642f5a348026dcf2c1176eda5915a4cbe716c7..66e1258364522c272e7872f5770a46f25b6e331f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_oper.h,v 1.27 2003/06/25 21:30:33 momjian Exp $
+ * $Id: parse_oper.h,v 1.28 2003/06/27 00:33:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,6 +45,7 @@ extern Operator ordering_oper(Oid argtype, bool noError);
 extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
 extern Oid equality_oper_funcid(Oid argtype);
 extern Oid ordering_oper_opid(Oid argtype);
+extern Oid ordering_oper_funcid(Oid argtype);
 
 /* Extract operator OID or underlying-function OID from an Operator tuple */
 extern Oid oprid(Operator op);
index 9cf35618ad74f0ca1a5a25ec3df63c0c7575a10a..8352f8180e14a03aed00f821055e32b101e878cd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: acl.h,v 1.54 2003/06/25 21:30:33 momjian Exp $
+ * $Id: acl.h,v 1.55 2003/06/27 00:33:26 tgl Exp $
  *
  * NOTES
  *   For backward-compatibility purposes we have to allow there
@@ -192,6 +192,7 @@ extern Datum aclinsert(PG_FUNCTION_ARGS);
 extern Datum aclremove(PG_FUNCTION_ARGS);
 extern Datum aclcontains(PG_FUNCTION_ARGS);
 extern Datum makeaclitem(PG_FUNCTION_ARGS);
+extern Datum aclitem_eq(PG_FUNCTION_ARGS);
 
 /*
  * prototypes for functions in aclchk.c
index 23a32d3459e909e471634d0a32d946e9aa3dd1f1..0d99816c246d48dd82a709c04ec4be9e4435895f 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: array.h,v 1.40 2003/06/25 21:30:33 momjian Exp $
+ * $Id: array.h,v 1.41 2003/06/27 00:33:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,37 @@ typedef struct
    Oid         elemtype;       /* element type OID */
 } ArrayType;
 
+typedef struct ArrayBuildState
+{
+   MemoryContext mcontext;     /* where all the temp stuff is kept */
+   Datum      *dvalues;        /* array of accumulated Datums */
+   /*
+    * The allocated size of dvalues[] is always a multiple of
+    * ARRAY_ELEMS_CHUNKSIZE
+    */
+#define ARRAY_ELEMS_CHUNKSIZE  64
+   int         nelems;         /* number of valid Datums in dvalues[] */
+   Oid         element_type;   /* data type of the Datums */
+   int16       typlen;         /* needed info about datatype */
+   bool        typbyval;
+   char        typalign;
+} ArrayBuildState;
+
+/*
+ * structure to cache type metadata needed for array manipulation
+ */
+typedef struct ArrayMetaState
+{
+   Oid             element_type;
+   int16           typlen;
+   bool            typbyval;
+   char            typalign;
+   char            typdelim;
+   Oid             typelem;
+   Oid             typiofunc;
+   FmgrInfo        proc;
+} ArrayMetaState;
+
 /*
  * fmgr macros for array objects
  */
@@ -86,11 +117,15 @@ extern Datum array_recv(PG_FUNCTION_ARGS);
 extern Datum array_send(PG_FUNCTION_ARGS);
 extern Datum array_length_coerce(PG_FUNCTION_ARGS);
 extern Datum array_eq(PG_FUNCTION_ARGS);
+extern Datum array_ne(PG_FUNCTION_ARGS);
+extern Datum array_lt(PG_FUNCTION_ARGS);
+extern Datum array_gt(PG_FUNCTION_ARGS);
+extern Datum array_le(PG_FUNCTION_ARGS);
+extern Datum array_ge(PG_FUNCTION_ARGS);
+extern Datum btarraycmp(PG_FUNCTION_ARGS);
 extern Datum array_dims(PG_FUNCTION_ARGS);
 extern Datum array_lower(PG_FUNCTION_ARGS);
 extern Datum array_upper(PG_FUNCTION_ARGS);
-extern Datum array_assign(PG_FUNCTION_ARGS);
-extern Datum array_subscript(PG_FUNCTION_ARGS);
 extern Datum array_type_coerce(PG_FUNCTION_ARGS);
 
 extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
@@ -124,7 +159,14 @@ extern void deconstruct_array(ArrayType *array,
                  Oid elmtype,
                  int elmlen, bool elmbyval, char elmalign,
                  Datum **elemsp, int *nelemsp);
-
+extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
+                                        Datum dvalue, bool disnull,
+                                        Oid element_type,
+                                        MemoryContext rcontext);
+extern Datum makeArrayResult(ArrayBuildState *astate,
+                            MemoryContext rcontext);
+extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
+                              int *dims, int *lbs, MemoryContext rcontext);
 
 /*
  * prototypes for functions defined in arrayutils.c
@@ -141,12 +183,11 @@ extern int    mda_next_tuple(int n, int *curr, int *span);
 /*
  * prototypes for functions defined in array_userfuncs.c
  */
-extern Datum singleton_array(PG_FUNCTION_ARGS);
 extern Datum array_push(PG_FUNCTION_ARGS);
-extern Datum array_accum(PG_FUNCTION_ARGS);
 extern Datum array_cat(PG_FUNCTION_ARGS);
 
-extern ArrayType *create_singleton_array(Oid element_type,
+extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
+                                        Oid element_type,
                                         Datum element,
                                         int ndims);
 
index 958021eb1f0df4072280d526937ce117db7db38e..52b67ac88a937ea3cd0152bdec4910ee210a5022 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.222 2003/06/25 21:30:33 momjian Exp $
+ * $Id: builtins.h,v 1.223 2003/06/27 00:33:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -530,6 +530,8 @@ extern bool SplitIdentifierString(char *rawstring, char separator,
                      List **namelist);
 extern Datum replace_text(PG_FUNCTION_ARGS);
 extern Datum split_text(PG_FUNCTION_ARGS);
+extern Datum text_to_array(PG_FUNCTION_ARGS);
+extern Datum array_to_text(PG_FUNCTION_ARGS);
 extern Datum to_hex32(PG_FUNCTION_ARGS);
 extern Datum to_hex64(PG_FUNCTION_ARGS);
 extern Datum md5_text(PG_FUNCTION_ARGS);
index d7d3bba9d977218e780b7656744e1796400c7d85..4d8749befc6464ca2fd462c16d97a50e39c0da05 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.74 2003/06/25 21:30:33 momjian Exp $
+ * $Id: lsyscache.h,v 1.75 2003/06/27 00:33:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "access/htup.h"
 
+/* I/O function selector for get_type_io_data */
+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);
@@ -56,6 +65,14 @@ extern bool get_typbyval(Oid typid);
 extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
 extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
                     char *typalign);
+extern void get_type_io_data(Oid typid,
+                            IOFuncSelector which_func,
+                            int16 *typlen,
+                            bool *typbyval,
+                            char *typalign,
+                            char *typdelim,
+                            Oid *typelem,
+                            Oid *func);
 extern char get_typstorage(Oid typid);
 extern int32 get_typtypmod(Oid typid);
 extern Node *get_typdefault(Oid typid);
index 2d799705edc92eba5af01bc3f1253ca99f2af121..6b79bd1774730d668124c32afe4b20cdc2cd9f06 100644 (file)
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.240 2003/06/26 11:37:05 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.241 2003/06/27 00:33:26 tgl Exp $ */
 
 /* Copyright comment */
 %{
@@ -4597,7 +4597,7 @@ type_declaration: S_TYPEDEF
                $3.type_enum != ECPGt_char &&
                    $3.type_enum != ECPGt_unsigned_char &&
                atoi(this->type->type_index) >= 0)
-               mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+               mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
 
            types = this;
        }
@@ -5417,7 +5417,7 @@ ECPGTypedef: TYPE_P
                    $5.type_enum != ECPGt_char &&
                    $5.type_enum != ECPGt_unsigned_char &&
                    atoi(this->type->type_index) >= 0)
-                   mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+                   mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
 
                types = this;
            }
@@ -5484,7 +5484,7 @@ ECPGVar: SQL_VAR
 
                    default:
                        if (atoi(length) >= 0)
-                           mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+                           mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
 
                        if (atoi(dimension) < 0)
                            type = ECPGmake_simple_type($5.type_enum, make_str("1"));
index 5f2dd86bb55f15fedccc6e005aa36cd255b2118a..80406bbccc5124115b194c0d5c71098e104dcb05 100644 (file)
@@ -504,7 +504,7 @@ ECPGfree_type(struct ECPGtype * type)
                switch (type->u.element->type)
                {
                    case ECPGt_array:
-                       yyerror("internal error, found multi-dimensional array\n");
+                       yyerror("internal error, found multidimensional array\n");
                        break;
                    case ECPGt_struct:
                    case ECPGt_union:
index be96e18c0c5488d38bc3fe5ba0eafb644d39436c..9fa2ec8a6c9a3756975e9f8d93fb1c6cdcc295a6 100644 (file)
@@ -436,7 +436,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
    if (atoi(type_index) >= 0)
    {
        if (atoi(*length) >= 0)
-           mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+           mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
 
        *length = type_index;
    }
@@ -444,7 +444,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
    if (atoi(type_dimension) >= 0)
    {
        if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
-           mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+           mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
 
        if (atoi(*dimension) >= 0)
            *length = *dimension;
@@ -463,10 +463,10 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
        mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
 
    if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
-       mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+       mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
 
    if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
-       mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+       mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
 
    switch (type_enum)
    {
@@ -480,7 +480,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
            }
 
            if (atoi(*length) >= 0)
-               mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
+               mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
 
            break;
        case ECPGt_varchar:
@@ -525,7 +525,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
            }
 
            if (atoi(*length) >= 0)
-               mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
+               mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
 
            break;
    }
index 617cf09a9a00a1f6b36339e019a60a7c3c317443..9822294d19af9710131f64beaaaf579bfc12b8ff 100644 (file)
@@ -178,19 +178,13 @@ SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
 (1 row)
 
 -- functions
-SELECT singleton_array(42) AS "{42}";
- {42} 
-------
- {42}
-(1 row)
-
-SELECT array_append(singleton_array(42), 6) AS "{42,6}";
+SELECT array_append(array[42], 6) AS "{42,6}";
  {42,6} 
 --------
  {42,6}
 (1 row)
 
-SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
+SELECT array_prepend(6, array[42]) AS "{6,42}";
  {6,42} 
 --------
  {6,42}
@@ -214,24 +208,6 @@ SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
  {{3,4},{5,6},{1,2}}
 (1 row)
 
-SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
- 1.2 
------
- 1.2
-(1 row)
-
-SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
- {1.1,9.99,1.3} 
-----------------
- {1.1,9.99,1.3}
-(1 row)
-
-SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
- 9.99 
-------
- 9.99
-(1 row)
-
 -- operators
 SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
        a       
@@ -318,3 +294,24 @@ SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"
  {{{{{{a,bb,ccc}}}}}}
 (1 row)
 
+-- test indexes on arrays
+create temp table arr_tbl (f1 int[] unique);
+NOTICE:  CREATE TABLE / UNIQUE will create implicit index 'arr_tbl_f1_key' for table 'arr_tbl'
+insert into arr_tbl values ('{1,2,3}');
+insert into arr_tbl values ('{1,2}');
+-- failure expected:
+insert into arr_tbl values ('{1,2,3}');
+ERROR:  Cannot insert a duplicate key into unique index arr_tbl_f1_key
+insert into arr_tbl values ('{2,3,4}');
+insert into arr_tbl values ('{1,5,3}');
+insert into arr_tbl values ('{1,2,10}');
+set enable_seqscan to off;
+select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}';
+    f1    
+----------
+ {1,2,10}
+ {1,5,3}
+(2 rows)
+
+-- note: if above select doesn't produce the expected tuple order,
+-- then you didn't get an indexscan plan, and something is busted.
index 82eff24125df262f27466880cf96b500991134d1..8b18ffb9eb83bc46df5c6876cb395b009313225c 100644 (file)
@@ -130,15 +130,11 @@ SELECT ARRAY[ARRAY['hello'],ARRAY['world']];
 SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
 
 -- functions
-SELECT singleton_array(42) AS "{42}";
-SELECT array_append(singleton_array(42), 6) AS "{42,6}";
-SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
+SELECT array_append(array[42], 6) AS "{42,6}";
+SELECT array_prepend(6, array[42]) AS "{6,42}";
 SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
 SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
 SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
-SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
-SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
-SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
 
 -- operators
 SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
@@ -157,3 +153,17 @@ SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE";
 SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}";
 SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE";
 SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}";
+
+-- test indexes on arrays
+create temp table arr_tbl (f1 int[] unique);
+insert into arr_tbl values ('{1,2,3}');
+insert into arr_tbl values ('{1,2}');
+-- failure expected:
+insert into arr_tbl values ('{1,2,3}');
+insert into arr_tbl values ('{2,3,4}');
+insert into arr_tbl values ('{1,5,3}');
+insert into arr_tbl values ('{1,2,10}');
+set enable_seqscan to off;
+select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}';
+-- note: if above select doesn't produce the expected tuple order,
+-- then you didn't get an indexscan plan, and something is busted.