I think this rounds off the documentation updates that I owed for array
and polymorphic function/aggregate changes. Let me know if you think I
missed anything major. In any case, please apply.
Thanks,
Joe
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
retrieving revision 1.28
diff -c -r1.28 array.sgml
*** doc/src/sgml/array.sgml 27 Jun 2003 00:33:25 -0000 1.28
--- doc/src/sgml/array.sgml 7 Aug 2003 15:41:38 -0000
***************
*** 116,122 ****
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>
--- 116,122 ----
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>
***************
*** 206,224 ****
</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>
An array value can be replaced completely:
<programlisting>
--- 206,211 ----
***************
*** 233,247 ****
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>
--- 220,225 ----
***************
*** 256,278 ****
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>
An array can be enlarged by assigning to an element adjacent to
those already present, or by assigning to a slice that is adjacent
! to or overlaps the data already present. For example, if an array
! value currently has 4 elements, it will have five elements after an
! update that assigns to <literal>array[5]</>. Currently, enlargement in
! this fashion is only allowed for one-dimensional arrays, not
! multidimensional arrays.
</para>
<para>
--- 234,249 ----
WHERE name = 'Carol';
</programlisting>
</para>
<para>
An array can be enlarged by assigning to an element adjacent to
those already present, or by assigning to a slice that is adjacent
! to or overlaps the data already present. For example, if array
! <literal>myarray</> currently has 4 elements, it will have five
! elements after an update that assigns to <literal>myarray[5]</>.
! Currently, enlargement in this fashion is only allowed for one-dimensional
! arrays, not multidimensional arrays.
</para>
<para>
***************
*** 434,472 ****
</programlisting>
However, this quickly becomes tedious for large arrays, and is not
! helpful if the size of the array is unknown. Although it is not built
! into <productname>PostgreSQL</productname>,
! there is an extension available that defines new functions and
! operators for iterating over array values. Using this, the above
query could be:
<programlisting>
! SELECT * FROM sal_emp WHERE pay_by_quarter[1:4] *= 10000;
! </programlisting>
!
! To search the entire array (not just specified slices), you could
! use:
!
! <programlisting>
! SELECT * FROM sal_emp WHERE pay_by_quarter *= 10000;
</programlisting>
In addition, you could find rows where the array had all values
! equal to 10 000 with:
<programlisting>
! SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
</programlisting>
- To install this optional module, look in the
- <filename>contrib/array</filename> directory of the
- <productname>PostgreSQL</productname> source distribution.
</para>
<tip>
<para>
Arrays are not sets; using arrays in the manner described in the
! previous paragraph is often a sign of database misdesign. The
array field should generally be split off into a separate table.
Tables can obviously be searched easily.
</para>
--- 405,431 ----
</programlisting>
However, this quickly becomes tedious for large arrays, and is not
! helpful if the size of the array is unknown. An alternative method is
! described in <xref linkend="functions-comparisons">. Using this, the above
query could be:
<programlisting>
! SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter);
</programlisting>
In addition, you could find rows where the array had all values
! equal to 10000 with:
<programlisting>
! SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter);
</programlisting>
</para>
<tip>
<para>
Arrays are not sets; using arrays in the manner described in the
! previous paragraph may be a sign of database misdesign. The
array field should generally be split off into a separate table.
Tables can obviously be searched easily.
</para>
***************
*** 498,513 ****
<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
--- 457,473 ----
<para>
As illustrated earlier in this chapter, arrays may also be represented
! in many cases 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
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.164
diff -c -r1.164 func.sgml
*** doc/src/sgml/func.sgml 4 Aug 2003 14:00:13 -0000 1.164
--- doc/src/sgml/func.sgml 7 Aug 2003 15:41:38 -0000
***************
*** 7044,7071 ****
<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>
--- 7044,7110 ----
<tbody>
<row>
<entry> <literal>=</literal> </entry>
! <entry>equal</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>not equal</entry>
+ <entry><literal>ARRAY[1,2,3] != ARRAY[1,2,4]</literal></entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><</literal> </entry>
+ <entry>less than</entry>
+ <entry><literal>ARRAY[1,2,3] < ARRAY[1,2,4]</literal></entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>></literal> </entry>
+ <entry>greater than</entry>
+ <entry><literal>ARRAY[1,4,3] > ARRAY[1,2,4]</literal></entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><=</literal> </entry>
+ <entry>less than or equal</entry>
+ <entry><literal>ARRAY[1,2,3] <= ARRAY[1,2,3]</literal></entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>>=</literal> </entry>
+ <entry>greater than or equal</entry>
+ <entry><literal>ARRAY[1,4,3] >= ARRAY[1,4,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>
Index: doc/src/sgml/plpgsql.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/plpgsql.sgml,v
retrieving revision 1.19
diff -c -r1.19 plpgsql.sgml
*** doc/src/sgml/plpgsql.sgml 28 May 2003 16:03:55 -0000 1.19
--- doc/src/sgml/plpgsql.sgml 7 Aug 2003 18:12:23 -0000
***************
*** 464,482 ****
</programlisting>
</para>
<sect2 id="plpgsql-declaration-aliases">
! <title>Aliases for Function Parameters</title>
<synopsis>
<replaceable>name</replaceable> ALIAS FOR $<replaceable>n</replaceable>;
</synopsis>
<para>
- Parameters passed to functions are named with the identifiers
- <literal>$1</literal>, <literal>$2</literal>,
- etc. Optionally, aliases can be declared for <literal>$<replaceable>n</replaceable></literal>
- parameter names for increased readability. Either the alias or the
- numeric identifier can then be used to refer to the parameter value.
Some examples:
<programlisting>
CREATE FUNCTION sales_tax(real) RETURNS real AS '
--- 464,512 ----
</programlisting>
</para>
+ <sect2 id="plpgsql-polymorphic-functions">
+ <title>Polymorphic <acronym>PL/pgSQL</acronym> Functions</title>
+
+ <para>
+ <acronym>PL/pgSQL</acronym> Functions may be specified to accept, and
+ optionally return, the types <type>anyelement</type> or
+ <type>anyarray</type>, otherwise known as polymorphic types.
+ See <xref linkend="xfunc"> for a more detailed explanation
+ of polymorphic functions. An example is shown in
+ <xref linkend="plpgsql-declaration-aliases">
+ </para>
+ </sect2>
+
<sect2 id="plpgsql-declaration-aliases">
! <title>Default Identifiers and Aliases</title>
!
! <para>
! Parameters passed to functions are named with the identifiers
! <literal>$1</literal>, <literal>$2</literal>, etc.
! </para>
!
! <para>
! When the return type of a <application>PL/pgSQL</application>
! function is declared a polymorphic type, <type>anyelement</type>
! or <type>anyarray</type>, the return value is also named with an
! identifier, <literal>$0</literal>. It is initialized to NULL, but
! may be used to hold the return value as calculated by the function.
! <literal>$0</literal> can be used by polymorphic functions to discover
! their own return type, as discussed in
! <xref linkend="plpgsql-declaration-type">
! </para>
!
! <para>
! Aliases can optionally be declared for <literal>$<replaceable>n</replaceable>
! </literal> identifiers for increased readability. Either the alias or the
! numeric identifier can then be used to refer to the value.
! </para>
<synopsis>
<replaceable>name</replaceable> ALIAS FOR $<replaceable>n</replaceable>;
</synopsis>
<para>
Some examples:
<programlisting>
CREATE FUNCTION sales_tax(real) RETURNS real AS '
***************
*** 505,510 ****
--- 535,553 ----
RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
' LANGUAGE plpgsql;
+
+ CREATE FUNCTION add_many_fields(anyelement, anyelement, anyelement)
+ RETURNS anyelement AS '
+ DECLARE
+ result ALIAS FOR $0;
+ first ALIAS FOR $1;
+ second ALIAS FOR $2;
+ third ALIAS FOR $3;
+ BEGIN
+ result := first + second + third;
+ RETURN result;
+ END;
+ ' LANGUAGE plpgsql;
</programlisting>
</para>
</sect2>
***************
*** 536,541 ****
--- 579,593 ----
from <type>integer</type> to <type>real</type>), you may not need
to change your function definition.
</para>
+
+ <para>
+ Similarly, using <literal>%TYPE</literal> allows you to define variables
+ within your function, without needing to know the data type of the
+ structure you are referencing in advance. This is important in polymorphic
+ functions in which the data type of the referenced item may change
+ from one call to the next.
+ </para>
+
</sect2>
<sect2 id="plpgsql-declaration-rowtypes">
Index: doc/src/sgml/xaggr.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/xaggr.sgml,v
retrieving revision 1.20
diff -c -r1.20 xaggr.sgml
*** doc/src/sgml/xaggr.sgml 10 Apr 2003 01:22:44 -0000 1.20
--- doc/src/sgml/xaggr.sgml 7 Aug 2003 18:27:53 -0000
***************
*** 34,39 ****
--- 34,48 ----
</para>
<para>
+ <acronym>Aggregate</acronym> Functions may use polymorphic
+ <firstterm>state transition functions</firstterm> or
+ <firstterm>final functions</firstterm>. See <xref linkend="xfunc">
+ for a more detailed explanation of polymorphic functions.
+ <acronym>Aggregate</acronym> Functions may also be specified with a
+ polymorphic base type and state type.
+ </para>
+
+ <para>
If we define an aggregate that does not use a final function,
we have an aggregate that computes a running function of
the column values from each row. <function>sum</> is an
***************
*** 107,112 ****
--- 116,154 ----
finalfunc = float8_avg,
initcond = '{0,0}'
);
+ </programlisting>
+ </para>
+
+ <para>
+ <function>array_accum</> is an example of a polymorphic aggregate:
+
+ <programlisting>
+ CREATE AGGREGATE array_accum (
+ sfunc = array_append,
+ basetype = anyelement,
+ stype = anyarray,
+ initcond = '{}'
+ );
+ </programlisting>
+
+ Here's the output using two different runtime data types as arguments:
+
+ <programlisting>
+ SELECT attrelid::regclass, array_accum(attname)
+ FROM pg_attribute WHERE attnum > 0
+ AND attrelid = 'pg_user'::regclass GROUP BY attrelid;
+ attrelid | array_accum
+ ----------+-----------------------------------------------------------------------------
+ pg_user | {usename,usesysid,usecreatedb,usesuper,usecatupd,passwd,valuntil,useconfig}
+ (1 row)
+
+ SELECT attrelid::regclass, array_accum(atttypid)
+ FROM pg_attribute WHERE attnum > 0
+ AND attrelid = 'pg_user'::regclass GROUP BY attrelid;
+ attrelid | array_accum
+ ----------+------------------------------
+ pg_user | {19,23,16,16,16,25,702,1009}
+ (1 row)
</programlisting>
</para>
Index: doc/src/sgml/xfunc.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/xfunc.sgml,v
retrieving revision 1.70
diff -c -r1.70 xfunc.sgml
*** doc/src/sgml/xfunc.sgml 25 Jul 2003 20:17:49 -0000 1.70
--- doc/src/sgml/xfunc.sgml 7 Aug 2003 18:12:10 -0000
***************
*** 47,56 ****
</para>
<para>
It's easiest to define <acronym>SQL</acronym>
! functions, so we'll start with those. Examples in this section
! can also be found in <filename>funcs.sql</filename>
! and <filename>funcs.c</filename> in the tutorial directory.
</para>
<para>
--- 47,76 ----
</para>
<para>
+ Many kinds of functions can be specified to accept, and
+ optionally return, the types <type>anyelement</type> or
+ <type>anyarray</type>, otherwise known as polymorphic types.
+ These datatypes are tied to each other and resolved to a deterministic
+ type at runtime. Each position (i.e. either argument or return type)
+ defined as <type>anyelement</type> can have any data type at runtime,
+ but they must all be the <emphasis>same</emphasis> runtime type. Each
+ position defined as <type>anyarray</type> can have any array data type
+ at runtime, but similarly they must all be the same. If there are
+ positions declared <type>anyarray</type> and others declared
+ <type>anyelement</type>, the runtime array type in the
+ <type>anyarray</type> positions must be an array of the runtime type
+ at the <type>anyelement</type> positions.
+ </para>
+
+ <para>
+ See the individual sections for each type of function to determine
+ if polymorphic functions are supported for that language.
+ </para>
+
+ <para>
It's easiest to define <acronym>SQL</acronym>
! functions, so we'll start with those. Some examples in this section
! can also be found in <filename>funcs.c</filename> in the tutorial directory.
</para>
<para>
***************
*** 383,388 ****
--- 403,470 ----
</sect2>
<sect2>
+ <title>Polymorphic <acronym>SQL</acronym> Functions</title>
+
+ <para>
+ <acronym>SQL</acronym> Functions may be specified to accept, and
+ optionally return, the types <type>anyelement</type> or
+ <type>anyarray</type>, otherwise known as polymorphic types.
+ See <xref linkend="xfunc"> for a more detailed explanation
+ of polymorphic functions. Here is a polymorphic function
+ <function>make_array</function> that builds up an array from two
+ arbitrary data type elements:
+ <screen>
+ CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS '
+ SELECT ARRAY[$1, $2];
+ ' LANGUAGE SQL;
+
+ SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;
+ intarray | textarray
+ ----------+-----------
+ {1,2} | {a,b}
+ (1 row)
+ </screen>
+ </para>
+
+ <para>
+ Notice the use of the typecast <literal>'a'::text</literal>
+ to specify a runtime <type>text</type> type. This is
+ required if the runtime type would otherwise be resolved as
+ <type>unknown</type>, because there is currently no way
+ to delay resolution of the element type to the time of array
+ creation, and array of <type>unknown</type> is not a valid type.
+ Without the typecast, you will get errors like this:
+ <screen>
+ <computeroutput>
+ ERROR: could not determine ANYARRAY/ANYELEMENT type because input is UNKNOWN
+ </computeroutput>
+ </screen>
+ </para>
+
+ <para>
+ It is permitted to have polymorphic arguments with a deterministic
+ return type, but the converse is not. For example:
+ <screen>
+ CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS bool AS '
+ SELECT $1 > $2;
+ ' LANGUAGE SQL;
+
+ SELECT is_greater(1, 2);
+ is_greater
+ ------------
+ f
+ (1 row)
+
+ CREATE FUNCTION invalid_func() RETURNS anyelement AS '
+ SELECT 1;
+ ' LANGUAGE SQL;
+ ERROR: cannot determine result datatype
+ DETAIL: A function returning ANYARRAY or ANYELEMENT must have at least one argument of either type.
+ </screen>
+ </para>
+ </sect2>
+
+ <sect2>
<title><acronym>SQL</acronym> Functions as Table Sources</title>
<para>
***************
*** 1584,1589 ****
--- 1666,1793 ----
AS '<replaceable>DIRECTORY</replaceable>/funcs', 'c_overpaid'
LANGUAGE C;
</programlisting>
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Polymorphic Arguments and Return Types</title>
+
+ <para>
+ C-Language functions may be specified to accept, and
+ optionally return, the types <type>anyelement</type> or
+ <type>anyarray</type>, otherwise known as polymorphic types.
+ See <xref linkend="xfunc"> for a more detailed explanation
+ of polymorphic functions. When function arguments or return types
+ are defined as polymorphic types, the function author cannot know
+ in advance what data type it will be called with, or
+ need to return. There are two exported routines in fmgr.c available
+ to allow a user defined function to discover the actual data types
+ of its arguments and the type it is expected to return. The routines are
+ declared in
+ <programlisting>
+ #include "fmgr.h"
+ </programlisting>
+ and are called get_fn_expr_rettype(FmgrInfo *flinfo) and
+ get_fn_expr_argtype(FmgrInfo *flinfo, int argnum). The structure
+ flinfo is normally accessed as fcinfo->flinfo. The parameter argnum
+ is zero based.
+ </para>
+
+ <para>
+ For example, suppose we want to write a function to accept a single
+ element of any type, and return a one-dimensional array of that type:
+
+ <programlisting>
+ PG_FUNCTION_INFO_V1(make_array);
+ Datum
+ make_array(PG_FUNCTION_ARGS)
+ {
+ ArrayType *result;
+ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+ Datum element;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int ndims;
+ int dims[MAXDIM];
+ int lbs[MAXDIM];
+
+ /* get the provided element */
+ element = PG_GETARG_DATUM(0);
+
+ /* we have one dimension */
+ ndims = 1;
+ /* and one element */
+ dims[0] = 1;
+ /* and lower bound is 1 */
+ lbs[0] = 1;
+
+ /* get required info about the element type */
+ get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+
+ /* now build the array */
+ result = construct_md_array(&element, ndims, dims, lbs,
+ element_type, typlen, typbyval, typalign);
+
+ PG_RETURN_ARRAYTYPE_P(result);
+ }
+ </programlisting>
+ </para>
+
+ <para>
+ The following command declares the function
+ <function>make_array</function> in SQL:
+
+ <programlisting>
+ CREATE FUNCTION make_array(anyelement)
+ RETURNS anyarray
+ AS '<replaceable>DIRECTORY</replaceable>/funcs', 'make_array'
+ LANGUAGE 'C' STRICT;
+ </programlisting>
+ </para>
+
+ <para>
+ The <function>make_array</function> function is then used as
+ in the following:
+ <programlisting>
+ select make_array('a'::text);
+ make_array
+ ------------
+ {a}
+ (1 row)
+
+ select make_array(1);
+ make_array
+ ------------
+ {1}
+ (1 row)
+
+ select make_array(1.1);
+ make_array
+ ------------
+ {1.1}
+ (1 row)
+ </programlisting>
+ </para>
+
+ <para>
+ Notice the use of the typecast <literal>'a'::text</literal>
+ to specify a runtime <type>text</type> type. This is
+ required if the runtime type would otherwise be resolved as
+ <type>unknown</type>, because there is currently no way
+ to delay resolution of the element type to the time of array
+ creation, and array of <type>unknown</type> is not a valid type.
+ Without the typecast, you will get errors like this:
+ <screen>
+ <computeroutput>
+ ERROR: could not determine ANYARRAY/ANYELEMENT type because input is UNKNOWN
+ </computeroutput>
+ </screen>
+ </para>
+
+ <para>
+ It is permitted to have polymorphic arguments with a deterministic
+ return type, but the converse is not.
</para>
</sect2>