]> granicus.if.org Git - postgresql/commitdiff
Support "variadic" functions, which can accept a variable number of arguments
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 16 Jul 2008 01:30:23 +0000 (01:30 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 16 Jul 2008 01:30:23 +0000 (01:30 +0000)
so long as all the trailing arguments are of the same (non-array) type.
The function receives them as a single array argument (which is why they
have to all be the same type).

It might be useful to extend this facility to aggregates, but this patch
doesn't do that.

This patch imposes a noticeable slowdown on function lookup --- a follow-on
patch will fix that by adding a redundant column to pg_proc.

Pavel Stehule

38 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/extend.sgml
doc/src/sgml/plpgsql.sgml
doc/src/sgml/ref/alter_function.sgml
doc/src/sgml/ref/comment.sgml
doc/src/sgml/ref/create_function.sgml
doc/src/sgml/ref/drop_function.sgml
doc/src/sgml/typeconv.sgml
doc/src/sgml/xfunc.sgml
src/backend/catalog/information_schema.sql
src/backend/catalog/namespace.c
src/backend/catalog/pg_aggregate.c
src/backend/commands/functioncmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_utilcmd.c
src/backend/utils/adt/regproc.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/fmgr/funcapi.c
src/bin/pg_dump/pg_dump.c
src/bin/psql/describe.c
src/include/catalog/catversion.h
src/include/catalog/namespace.h
src/include/catalog/pg_proc.h
src/include/nodes/parsenodes.h
src/include/parser/parse_func.h
src/interfaces/ecpg/preproc/preproc.y
src/pl/plpgsql/src/pl_comp.c
src/pl/plpython/plpython.c
src/test/regress/expected/plpgsql.out
src/test/regress/expected/polymorphism.out
src/test/regress/sql/plpgsql.sql
src/test/regress/sql/polymorphism.sql

index 76198b0f832ba10c39a49619b8111e952133418b..7accea0f76c721bd2aad3cd7a0f074ac2ef2888b 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.168 2008/07/14 00:51:45 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.169 2008/07/16 01:30:21 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
       <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
       <entry>
        An array with the data types of the function arguments.  This includes
-       only input arguments (including <literal>INOUT</literal> arguments), and thus represents
+       only input arguments (including <literal>INOUT</literal> and
+       <literal>VARIADIC</> arguments), and thus represents
        the call signature of the function
       </entry>
      </row>
       <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
       <entry>
        An array with the data types of the function arguments.  This includes
-       all arguments (including <literal>OUT</literal> and <literal>INOUT</literal> arguments); however, if all the
-       arguments are IN arguments, this field will be null.
+       all arguments (including <literal>OUT</literal> and
+       <literal>INOUT</literal> arguments); however, if all the
+       arguments are <literal>IN</literal> arguments, this field will be null.
        Note that subscripting is 1-based, whereas for historical reasons
        <structfield>proargtypes</> is subscripted from 0
       </entry>
         An array with the modes of the function arguments, encoded as
         <literal>i</literal> for <literal>IN</> arguments,
         <literal>o</literal> for <literal>OUT</> arguments,
-        <literal>b</literal> for <literal>INOUT</> arguments.
-        If all the arguments are <literal>IN</literal> arguments, this field will be null.
+        <literal>b</literal> for <literal>INOUT</> arguments,
+        <literal>v</literal> for <literal>VARIADIC</> arguments.
+        If all the arguments are <literal>IN</literal> arguments,
+        this field will be null.
         Note that subscripts correspond to positions of
         <structfield>proallargtypes</> not <structfield>proargtypes</>
       </entry>
index e2805b4191753d30efb2227cb13e3213d38f2643..caeda7e75cad27d6d0060ddce35aec033fee785d 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.36 2007/08/31 21:33:48 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.37 2008/07/16 01:30:21 tgl Exp $ -->
 
  <chapter id="extend">
   <title>Extending <acronym>SQL</acronym></title>
      is equivalent to declaring it as <literal>f(anyenum, anyenum)</>:
      both actual arguments have to be the same enum type.
     </para>
+
+    <para>
+     A variadic function (one taking a variable number of arguments, as in
+     <xref linkend="xfunc-sql-variadic-functions">) can be
+     polymorphic: this is accomplished by declaring its last parameter as
+     <literal>VARIADIC</> <type>anyarray</>.  For purposes of argument
+     matching and determining the actual result type, such a function behaves
+     the same as if you had written the appropriate number of
+     <type>anynonarray</> parameters.
+    </para>
    </sect2>
   </sect1>
 
index cd1531999b706ac911159a8020923f2ec4cd283f..42bd6048b68758c4278027b5ba88033136300856 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.131 2008/06/27 01:52:59 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.132 2008/07/16 01:30:21 tgl Exp $ -->
 
 <chapter id="plpgsql">
   <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
      calling query, as discussed in <xref linkend="queries-tablefunctions">.
     </para>
 
+    <para>
+     <application>PL/pgSQL</> functions can be declared to accept a variable
+     number of arguments by using the <literal>VARIADIC</> marker.  This
+     works exactly the same way as for SQL functions, as discussed in
+     <xref linkend="xfunc-sql-variadic-functions">.
+    </para>
+
     <para>
      <application>PL/pgSQL</> functions can also be declared to accept
      and return the polymorphic types
index bee2f6f4390f201be9b3c7fdc4cfe038bf7bb2eb..abedfe7d50becbb515d33d9dab028d2b22b7c3b3 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_function.sgml,v 1.15 2007/09/03 18:46:29 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_function.sgml,v 1.16 2008/07/16 01:30:21 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -81,13 +81,14 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
 
     <listitem>
      <para>
-      The mode of an argument: either <literal>IN</>, <literal>OUT</>,
-      or <literal>INOUT</>.  If omitted, the default is <literal>IN</>.
+      The mode of an argument: <literal>IN</>, <literal>OUT</>,
+      <literal>INOUT</>, or <literal>VARIADIC</>.
+      If omitted, the default is <literal>IN</>.
       Note that <command>ALTER FUNCTION</command> does not actually pay
       any attention to <literal>OUT</> arguments, since only the input
       arguments are needed to determine the function's identity.
-      So it is sufficient to list the <literal>IN</> and <literal>INOUT</>
-      arguments.
+      So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
+      and <literal>VARIADIC</> arguments.
      </para>
     </listitem>
    </varlistentry>
index c8993e915b8e683f4fc722f7feda32eeb7e6a8ae..2da64f87aca4ab5b9349aeb3a89af430845d4536 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.36 2007/08/21 21:08:47 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.37 2008/07/16 01:30:21 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -136,13 +136,14 @@ COMMENT ON
 
     <listitem>
      <para>
-      The mode of a function argument: either <literal>IN</>, <literal>OUT</>,
-      or <literal>INOUT</>.  If omitted, the default is <literal>IN</>.
+      The mode of a function argument: <literal>IN</>, <literal>OUT</>,
+      <literal>INOUT</>, or <literal>VARIADIC</>.
+      If omitted, the default is <literal>IN</>.
       Note that <command>COMMENT ON FUNCTION</command> does not actually pay
       any attention to <literal>OUT</> arguments, since only the input
       arguments are needed to determine the function's identity.
-      So it is sufficient to list the <literal>IN</> and <literal>INOUT</>
-      arguments.
+      So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
+      and <literal>VARIADIC</> arguments.
      </para>
     </listitem>
    </varlistentry>
index 8c542982d52ba32733dd658ef13bd9394ec021e0..18b9bf7beeac466b43ffa4fbe8b95e2b3129e51f 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.78 2007/09/11 00:06:41 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.79 2008/07/16 01:30:21 tgl Exp $
 -->
 
 <refentry id="SQL-CREATEFUNCTION">
@@ -101,8 +101,9 @@ CREATE [ OR REPLACE ] FUNCTION
 
      <listitem>
       <para>
-       The mode of an argument: either <literal>IN</>, <literal>OUT</>,
-       or <literal>INOUT</>.  If omitted, the default is <literal>IN</>.
+       The mode of an argument: <literal>IN</>, <literal>OUT</>,
+       <literal>INOUT</>, or <literal>VARIADIC</>.
+       If omitted, the default is <literal>IN</>.
       </para>
      </listitem>
     </varlistentry>
index bf39f5356ddff9f3b1c1294fcc0364c6707942cb..256cafe684f1e1bb2b28402024dd7cd74f5e1145 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.33 2007/01/31 23:26:03 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.34 2008/07/16 01:30:21 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -65,13 +65,14 @@ DROP FUNCTION [ IF EXISTS ] <replaceable class="parameter">name</replaceable> (
 
     <listitem>
      <para>
-      The mode of an argument: either <literal>IN</>, <literal>OUT</>,
-      or <literal>INOUT</>.  If omitted, the default is <literal>IN</>.
+      The mode of an argument: <literal>IN</>, <literal>OUT</>,
+      <literal>INOUT</>, or <literal>VARIADIC</>.
+      If omitted, the default is <literal>IN</>.
       Note that <command>DROP FUNCTION</command> does not actually pay
       any attention to <literal>OUT</> arguments, since only the input
       arguments are needed to determine the function's identity.
-      So it is sufficient to list the <literal>IN</> and <literal>INOUT</>
-      arguments.
+      So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
+      and <literal>VARIADIC</> arguments.
      </para>
     </listitem>
    </varlistentry>
index 451555cd022708b89f7c025e2a9746eafbe3e52a..4f04801210bdb79a182a551dc3ce948271a6e136 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/typeconv.sgml,v 1.54 2008/07/11 07:02:43 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/typeconv.sgml,v 1.55 2008/07/16 01:30:21 tgl Exp $ -->
 
 <chapter Id="typeconv">
 <title>Type Conversion</title>
@@ -503,6 +503,18 @@ different argument types are considered on an equal footing regardless of
 search path position.
 </para>
 </step>
+<step performance="optional">
+<para>
+If a function is declared with a <literal>VARIADIC</> array parameter, and
+the call does not use the <literal>VARIADIC</> keyword, then the function
+is treated as if the array parameter were replaced by one or more occurrences
+of its element type, as needed to match the call.  After such expansion the
+function might have effective argument types identical to some non-variadic
+function.  In that case the function appearing earlier in the search path is
+used, or if the two functions are in the same schema, the non-variadic one is
+selected.
+</para>
+</step>
 </substeps>
 </step>
 
index efee5d8d46914b57c6a52c1d514940e835e327f7..55ed719ec64c1701f22fbd8d6308a951349b3ff3 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.130 2007/11/10 20:14:36 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.131 2008/07/16 01:30:21 tgl Exp $ -->
 
  <sect1 id="xfunc">
   <title>User-Defined Functions</title>
@@ -495,7 +495,7 @@ SELECT getname(new_emp());
  None
 (1 row)
 </screen>
-    </para>     
+    </para>
 
     <para>
      Still another way to use a function that returns a composite type is to
@@ -505,7 +505,7 @@ SELECT getname(new_emp());
    </sect2>
 
    <sect2 id="xfunc-output-parameters">
-    <title>Functions with Output Parameters</title>
+    <title><acronym>SQL</> Functions with Output Parameters</title>
 
    <indexterm>
     <primary>function</primary>
@@ -578,9 +578,75 @@ DROP FUNCTION sum_n_product (int, int);
 
     <para>
      Parameters can be marked as <literal>IN</> (the default),
-     <literal>OUT</>, or <literal>INOUT</>.  An <literal>INOUT</>
+     <literal>OUT</>, <literal>INOUT</>, or <literal>VARIADIC</>.
+     An <literal>INOUT</>
      parameter serves as both an input parameter (part of the calling
      argument list) and an output parameter (part of the result record type).
+     <literal>VARIADIC</> parameters are input parameters, but are treated
+     specially as described next.
+    </para>
+   </sect2>
+
+   <sect2 id="xfunc-sql-variadic-functions">
+    <title><acronym>SQL</> Functions with Variable Numbers of Arguments</title>
+
+    <indexterm>
+     <primary>function</primary>
+     <secondary>variadic</secondary>
+    </indexterm>
+
+    <indexterm>
+     <primary>variadic function</primary>
+    </indexterm>
+
+    <para>
+     <acronym>SQL</acronym> functions can be declared to accept
+     variable numbers of arguments, so long as all the <quote>optional</>
+     arguments are of the same data type.  The optional arguments will be
+     passed to the function as an array.  The function is declared by
+     marking the last parameter as <literal>VARIADIC</>; this parameter
+     must be declared as being of an array type.  For example:
+
+<screen>
+CREATE FUNCTION mleast(VARIADIC numeric[]) RETURNS numeric AS $$
+    SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
+$$ LANGUAGE SQL;
+
+SELECT mleast(10, -1, 5, 4.4);
+ mleast 
+--------
+     -1
+(1 row)
+</screen>
+
+     Effectively, all the actual arguments at or beyond the
+     <literal>VARIADIC</> position are gathered up into a one-dimensional
+     array, as if you had written
+
+<screen>
+SELECT mleast(ARRAY[10, -1, 5, 4.4]);    -- doesn't work
+</screen>
+
+     You can't actually write that, though &mdash; or at least, it will
+     not match this function definition.  A parameter marked
+     <literal>VARIADIC</> matches one or more occurrences of its element
+     type, not of its own type.
+    </para>
+
+    <para>
+     Sometimes it is useful to be able to pass an already-constructed array
+     to a variadic function; this is particularly handy when one variadic
+     function wants to pass on its array parameter to another one.  You can
+     do that by specifying <literal>VARIADIC</> in the call:
+
+<screen>
+SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
+</screen>
+
+     This prevents expansion of the function's variadic parameter into its
+     element type, thereby allowing the array argument value to match
+     normally.  <literal>VARIADIC</> can only be attached to the last
+     actual argument of a function call.
     </para>
    </sect2>
 
@@ -795,13 +861,45 @@ DETAIL:  A function returning a polymorphic type must have at least one polymorp
      For example:
 <screen>
 CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray)
-AS 'select $1, array[$1,$1]' LANGUAGE sql;
+AS 'select $1, array[$1,$1]' LANGUAGE SQL;
 
 SELECT * FROM dup(22);
  f2 |   f3
 ----+---------
  22 | {22,22}
 (1 row)
+</screen>
+    </para>
+
+    <para>
+     Polymorphism can also be used with variadic functions.
+     For example:
+<screen>
+CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
+    SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
+$$ LANGUAGE SQL;
+
+SELECT anyleast(10, -1, 5, 4);
+ anyleast 
+----------
+       -1
+(1 row)
+
+SELECT anyleast('abc'::text, 'def');
+ anyleast 
+----------
+ abc
+(1 row)
+
+CREATE FUNCTION concat(text, VARIADIC anyarray) RETURNS text AS $$
+    SELECT array_to_string($2, $1);
+$$ LANGUAGE SQL;
+
+SELECT concat('|', 1, 4, 2);
+ concat 
+--------
+ 1|4|2
+(1 row)
 </screen>
     </para>
    </sect2>
@@ -852,6 +950,16 @@ CREATE FUNCTION test(smallint, double precision) RETURNS ...
     avoid the problem by not choosing conflicting names.
    </para>
 
+   <para>
+    Another possible conflict is between variadic and non-variadic functions.
+    For instance, it is possible to create both <literal>foo(numeric)</> and
+    <literal>foo(VARIADIC numeric[])</>.  In this case it is unclear which one
+    should be matched to a call providing a single numeric argument, such as
+    <literal>foo(10.1)</>.  The rule is that the function appearing
+    earlier in the search path is used, or if the two functions are in the
+    same schema, the non-variadic one is preferred.
+   </para>
+
    <para>
     When overloading C-language functions, there is an additional
     constraint: The C name of each function in the family of
@@ -2952,7 +3060,25 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
     LANGUAGE C IMMUTABLE;
 </programlisting>
     </para>
+
+    <para>
+     There is a variant of polymorphism that is only available to C-language
+     functions: they can be declared to take parameters of type
+     <literal>"any"</>.  (Note that this type name must be double-quoted,
+     since it's also a SQL reserved word.)  This works like
+     <type>anyelement</> except that it does not constrain different
+     <literal>"any"</> arguments to be the same type, nor do they help
+     determine the function's result type.  A C-language function can also
+     declare its final parameter to be <literal>VARIADIC "any"</>.  This will
+     match one or more actual arguments of any type (not necessarily the same
+     type).  These arguments will <emphasis>not</> be gathered into an array
+     as happens with normal variadic functions; they will just be passed to
+     the function separately.  The <function>PG_NARGS()</> macro and the
+     methods described above must be used to determine the number of actual
+     arguments and their types when using this feature.
+    </para>
    </sect2>
+
    <sect2>
     <title>Shared Memory and LWLocks</title>
 
index a9566d9d3f36b8fe73ce00253038688cbfa2c582..0e2452fa0f6ab7fc3c15ba0c20460c36289cfb6a 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (c) 2003-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.43 2008/01/01 19:45:48 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.44 2008/07/16 01:30:21 tgl Exp $
  */
 
 /*
@@ -1006,6 +1006,7 @@ CREATE VIEW parameters AS
                 WHEN proargmodes[(ss.x).n] = 'i' THEN 'IN'
                 WHEN proargmodes[(ss.x).n] = 'o' THEN 'OUT'
                 WHEN proargmodes[(ss.x).n] = 'b' THEN 'INOUT'
+                WHEN proargmodes[(ss.x).n] = 'v' THEN 'IN'
              END AS character_data) AS parameter_mode,
            CAST('NO' AS character_data) AS is_result,
            CAST('NO' AS character_data) AS as_locator,
index dfaea41cfe4846e213d564d14bf14e84b0c0ce2c..48b8ee45e6b2bdbd10673dc17d5a243b94b5af52 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.107 2008/07/01 02:09:34 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.108 2008/07/16 01:30:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
+#include "parser/parse_func.h"
 #include "storage/backendid.h"
 #include "storage/ipc.h"
 #include "utils/acl.h"
@@ -561,24 +562,36 @@ TypeIsVisible(Oid typid)
  *             retrieve a list of the possible matches.
  *
  * If nargs is -1, we return all functions matching the given name,
- * regardless of argument count.
+ * regardless of argument count. (expand_variadic must be false in this case.)
+ *
+ * If expand_variadic is true, then variadic functions having the same number
+ * or fewer arguments will be retrieved, with the variadic argument and any
+ * additional argument positions filled with the variadic element type.
+ * nvargs in the returned struct is set to the number of such arguments.
+ * If expand_variadic is false, variadic arguments are not treated specially,
+ * and the returned nvargs will always be zero.
  *
  * We search a single namespace if the function name is qualified, else
  * all namespaces in the search path.  The return list will never contain
  * multiple entries with identical argument lists --- in the multiple-
  * namespace case, we arrange for entries in earlier namespaces to mask
- * identical entries in later namespaces.
+ * identical entries in later namespaces.  We also arrange for non-variadic
+ * functions to mask variadic ones if the expanded argument list is the same.
  */
 FuncCandidateList
-FuncnameGetCandidates(List *names, int nargs)
+FuncnameGetCandidates(List *names, int nargs, bool expand_variadic)
 {
        FuncCandidateList resultList = NULL;
+       bool            any_variadic = false;
        char       *schemaname;
        char       *funcname;
        Oid                     namespaceId;
        CatCList   *catlist;
        int                     i;
 
+       /* check for caller error */
+       Assert(nargs >= 0 || !expand_variadic);
+
        /* deconstruct the name list */
        DeconstructQualifiedName(names, &schemaname, &funcname);
 
@@ -604,11 +617,57 @@ FuncnameGetCandidates(List *names, int nargs)
                HeapTuple       proctup = &catlist->members[i]->tuple;
                Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
                int                     pronargs = procform->pronargs;
+               int                     effective_nargs;
                int                     pathpos = 0;
+               bool            variadic = false;
+               Oid                     va_elem_type = InvalidOid;
                FuncCandidateList newResult;
 
+               /*
+                * Check if function is variadic, and get variadic element type if so.
+                * If expand_variadic is false, we can just ignore variadic-ness.
+                *
+                * XXX it's annoying to inject something as expensive as this even
+                * when there are no variadic functions involved.  Find a better way.
+                */
+               if (expand_variadic)
+               {
+                       Datum           proargmodes;
+                       bool            isnull;
+
+                       proargmodes = SysCacheGetAttr(PROCOID, proctup,
+                                                                                 Anum_pg_proc_proargmodes, &isnull);
+                       if (!isnull)
+                       {
+                               ArrayType       *ar = DatumGetArrayTypeP(proargmodes);
+                               char            *argmodes;
+                               int     j;
+
+                               argmodes = ARR_DATA_PTR(ar);
+                               j = ARR_DIMS(ar)[0] - 1;
+                               if (j >= 0 && argmodes[j] == PROARGMODE_VARIADIC)
+                               {
+                                       variadic = any_variadic = true;
+                                       switch (procform->proargtypes.values[j])
+                                       {
+                                               case ANYOID:
+                                                       va_elem_type = ANYOID;
+                                                       break;
+                                               case ANYARRAYOID:
+                                                       va_elem_type = ANYELEMENTOID;
+                                                       break;
+                                               default:
+                                                       va_elem_type = get_element_type(procform->proargtypes.values[j]);
+                                                       Assert(OidIsValid(va_elem_type));
+                                                       break;
+                                       }
+                               }
+                       }
+               }
+
                /* Ignore if it doesn't match requested argument count */
-               if (nargs >= 0 && pronargs != nargs)
+               if (nargs >= 0 &&
+                       (variadic ? (pronargs > nargs) : (pronargs != nargs)))
                        continue;
 
                if (OidIsValid(namespaceId))
@@ -616,7 +675,6 @@ FuncnameGetCandidates(List *names, int nargs)
                        /* Consider only procs in specified namespace */
                        if (procform->pronamespace != namespaceId)
                                continue;
-                       /* No need to check args, they must all be different */
                }
                else
                {
@@ -635,28 +693,63 @@ FuncnameGetCandidates(List *names, int nargs)
                        }
                        if (nsp == NULL)
                                continue;               /* proc is not in search path */
+               }
 
+               /*
+                * We must compute the effective argument list so that we can easily
+                * compare it to earlier results.  We waste a palloc cycle if it gets
+                * masked by an earlier result, but really that's a pretty infrequent
+                * case so it's not worth worrying about.
+                */
+               effective_nargs = Max(pronargs, nargs);
+               newResult = (FuncCandidateList)
+                       palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
+                                  + effective_nargs * sizeof(Oid));
+               newResult->pathpos = pathpos;
+               newResult->oid = HeapTupleGetOid(proctup);
+               newResult->nargs = effective_nargs;
+               memcpy(newResult->args, procform->proargtypes.values,
+                          pronargs * sizeof(Oid));
+               if (variadic)
+               {
+                       int             i;
+
+                       newResult->nvargs = effective_nargs - pronargs + 1;
+                       /* Expand variadic argument into N copies of element type */
+                       for (i = pronargs - 1; i < effective_nargs; i++)
+                               newResult->args[i] = va_elem_type;
+               }
+               else
+                       newResult->nvargs = 0;
+
+               /*
+                * Does it have the same arguments as something we already accepted?
+                * If so, decide which one to keep.  We can skip this check for the
+                * single-namespace case if no variadic match has been made, since
+                * then the unique index on pg_proc guarantees all the matches have
+                * different argument lists.
+                */
+               if (any_variadic || !OidIsValid(namespaceId))
+               {
                        /*
-                        * Okay, it's in the search path, but does it have the same
-                        * arguments as something we already accepted?  If so, keep only
-                        * the one that appears earlier in the search path.
-                        *
                         * If we have an ordered list from SearchSysCacheList (the normal
                         * case), then any conflicting proc must immediately adjoin this
                         * one in the list, so we only need to look at the newest result
                         * item.  If we have an unordered list, we have to scan the whole
-                        * result list.
+                        * result list.  Also, if either the current candidate or any
+                        * previous candidate is a variadic match, we can't assume that
+                        * conflicts are adjacent.
                         */
                        if (resultList)
                        {
                                FuncCandidateList prevResult;
 
-                               if (catlist->ordered)
+                               if (catlist->ordered && !any_variadic)
                                {
-                                       if (pronargs == resultList->nargs &&
-                                               memcmp(procform->proargtypes.values,
+                                       if (effective_nargs == resultList->nargs &&
+                                               memcmp(newResult->args,
                                                           resultList->args,
-                                                          pronargs * sizeof(Oid)) == 0)
+                                                          effective_nargs * sizeof(Oid)) == 0)
                                                prevResult = resultList;
                                        else
                                                prevResult = NULL;
@@ -667,22 +760,58 @@ FuncnameGetCandidates(List *names, int nargs)
                                                 prevResult;
                                                 prevResult = prevResult->next)
                                        {
-                                               if (pronargs == prevResult->nargs &&
-                                                       memcmp(procform->proargtypes.values,
+                                               if (effective_nargs == prevResult->nargs &&
+                                                       memcmp(newResult->args,
                                                                   prevResult->args,
-                                                                  pronargs * sizeof(Oid)) == 0)
+                                                                  effective_nargs * sizeof(Oid)) == 0)
                                                        break;
                                        }
                                }
                                if (prevResult)
                                {
-                                       /* We have a match with a previous result */
-                                       Assert(pathpos != prevResult->pathpos);
+                                       /*
+                                        * We have a match with a previous result.  Prefer the
+                                        * one that's earlier in the search path.
+                                        */
                                        if (pathpos > prevResult->pathpos)
+                                       {
+                                               pfree(newResult);
                                                continue;               /* keep previous result */
+                                       }
+                                       else if (pathpos == prevResult->pathpos)
+                                       {
+                                               /*
+                                                * With variadic functions we could have, for example,
+                                                * both foo(numeric) and foo(variadic numeric[]) in
+                                                * the same namespace; if so we prefer the
+                                                * non-variadic match on efficiency grounds.  It's
+                                                * also possible to have conflicting variadic
+                                                * functions, such as foo(numeric, variadic numeric[])
+                                                * and foo(variadic numeric[]).  If you're silly
+                                                * enough to do that, we throw an error.  (XXX It'd be
+                                                * better to detect such conflicts when the functions
+                                                * are created.)
+                                                */
+                                               if (variadic)
+                                               {
+                                                       if (prevResult->nvargs > 0)
+                                                               ereport(ERROR,
+                                                                               (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+                                                                                errmsg("variadic function %s conflicts with another",
+                                                                                               func_signature_string(names, pronargs,
+                                                                                                                                         procform->proargtypes.values))));
+                                                       /* else, previous result wasn't variadic */
+                                                       pfree(newResult);
+                                                       continue;       /* keep previous result */
+                                               }
+                                               /* non-variadic can replace a previous variadic */
+                                               Assert(prevResult->nvargs > 0);
+                                       }
                                        /* replace previous result */
                                        prevResult->pathpos = pathpos;
-                                       prevResult->oid = HeapTupleGetOid(proctup);
+                                       prevResult->oid = newResult->oid;
+                                       prevResult->nvargs = newResult->nvargs;
+                                       pfree(newResult);
                                        continue;       /* args are same, of course */
                                }
                        }
@@ -691,15 +820,6 @@ FuncnameGetCandidates(List *names, int nargs)
                /*
                 * Okay to add it to result list
                 */
-               newResult = (FuncCandidateList)
-                       palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
-                                  + pronargs * sizeof(Oid));
-               newResult->pathpos = pathpos;
-               newResult->oid = HeapTupleGetOid(proctup);
-               newResult->nargs = pronargs;
-               memcpy(newResult->args, procform->proargtypes.values,
-                          pronargs * sizeof(Oid));
-
                newResult->next = resultList;
                resultList = newResult;
        }
@@ -755,7 +875,8 @@ FunctionIsVisible(Oid funcid)
 
                visible = false;
 
-               clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs);
+               clist = FuncnameGetCandidates(list_make1(makeString(proname)),
+                                                                         nargs, false);
 
                for (; clist; clist = clist->next)
                {
@@ -1023,6 +1144,7 @@ OpernameGetCandidates(List *names, char oprkind)
                newResult->pathpos = pathpos;
                newResult->oid = HeapTupleGetOid(opertup);
                newResult->nargs = 2;
+               newResult->nvargs = 0;
                newResult->args[0] = operform->oprleft;
                newResult->args[1] = operform->oprright;
                newResult->next = resultList;
index 1ff7261877f1ae74b096a9fda993abd5b290df47..e1c67ce5cd4cfce53263b858ccec33e4f8746547 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.93 2008/06/19 00:46:04 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.94 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -293,6 +293,7 @@ lookup_agg_function(List *fnName,
 {
        Oid                     fnOid;
        bool            retset;
+       int                     nvargs;
        Oid                *true_oid_array;
        FuncDetailCode fdresult;
        AclResult       aclresult;
@@ -305,8 +306,8 @@ lookup_agg_function(List *fnName,
         * function's return value.  it also returns the true argument types to
         * the function.
         */
-       fdresult = func_get_detail(fnName, NIL, nargs, input_types,
-                                                          &fnOid, rettype, &retset,
+       fdresult = func_get_detail(fnName, NIL, nargs, input_types, false,
+                                                          &fnOid, rettype, &retset, &nvargs,
                                                           &true_oid_array);
 
        /* only valid case is a normal function not returning a set */
index 9831692eea0a4fa82b54f2202fdbae63ce23459c..cb249d9c7d1987de4a6f47935d8a9f11b414e524 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.95 2008/07/12 10:44:56 petere Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.96 2008/07/16 01:30:22 tgl Exp $
  *
  * DESCRIPTION
  *       These routines take the parse tree and pick out the
@@ -173,6 +173,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
        Datum      *paramModes;
        Datum      *paramNames;
        int                     outCount = 0;
+       int                     varCount = 0;
        bool            have_names = false;
        ListCell   *x;
        int                     i;
@@ -228,15 +229,41 @@ examine_parameter_list(List *parameters, Oid languageOid,
                                         errmsg("functions cannot accept set arguments")));
 
                if (fp->mode != FUNC_PARAM_OUT)
+               {
+                       /* only OUT parameters can follow a VARIADIC parameter */
+                       if (varCount > 0)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                                errmsg("VARIADIC parameter must be the last input parameter")));
                        inTypes[inCount++] = toid;
+               }
 
-               if (fp->mode != FUNC_PARAM_IN)
+               if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
                {
                        if (outCount == 0)      /* save first OUT param's type */
                                *requiredResultType = toid;
                        outCount++;
                }
 
+               if (fp->mode == FUNC_PARAM_VARIADIC)
+               {
+                       varCount++;
+                       /* validate variadic parameter type */
+                       switch (toid)
+                       {
+                               case ANYARRAYOID:
+                               case ANYOID:
+                                       /* okay */
+                                       break;
+                               default:
+                                       if (!OidIsValid(get_element_type(toid)))
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                                                errmsg("VARIADIC parameter must be an array")));
+                                       break;
+                       }
+               }
+
                allTypes[i] = ObjectIdGetDatum(toid);
 
                paramModes[i] = CharGetDatum(fp->mode);
@@ -253,7 +280,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
        /* Now construct the proper outputs as needed */
        *parameterTypes = buildoidvector(inTypes, inCount);
 
-       if (outCount > 0)
+       if (outCount > 0 || varCount > 0)
        {
                *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
                                                                                         sizeof(Oid), true, 'i');
index a42c40327f2b25bdc1ca029eae8a0366328ee584..2e1ce4cb0d7bca52f23bd247d2bbc93cc9659a76 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.394 2008/05/16 23:36:05 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.395 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1651,6 +1651,7 @@ _copyFuncCall(FuncCall *from)
        COPY_NODE_FIELD(args);
        COPY_SCALAR_FIELD(agg_star);
        COPY_SCALAR_FIELD(agg_distinct);
+       COPY_SCALAR_FIELD(func_variadic);
        COPY_SCALAR_FIELD(location);
 
        return newnode;
index 435ee6a6afbbe4ac2d5c30c601fcbe8090dabbcd..41999226b6d625a40714a48876c074547d7ea5c9 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.323 2008/05/16 23:36:05 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.324 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1705,6 +1705,7 @@ _equalFuncCall(FuncCall *a, FuncCall *b)
        COMPARE_NODE_FIELD(args);
        COMPARE_SCALAR_FIELD(agg_star);
        COMPARE_SCALAR_FIELD(agg_distinct);
+       COMPARE_SCALAR_FIELD(func_variadic);
        COMPARE_SCALAR_FIELD(location);
 
        return true;
index 51b46a83edfb6ab3603562f4164a5598e87b85b9..a03063ce1e69f3c71d9640f12023219b38e5c9d8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.326 2008/04/29 14:59:16 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.327 2008/07/16 01:30:22 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -1610,6 +1610,7 @@ _outFuncCall(StringInfo str, FuncCall *node)
        WRITE_NODE_FIELD(args);
        WRITE_BOOL_FIELD(agg_star);
        WRITE_BOOL_FIELD(agg_distinct);
+       WRITE_BOOL_FIELD(func_variadic);
        WRITE_INT_FIELD(location);
 }
 
index 4e59e37da91f04cb777705c441571c1b2a800178..70bbe940afd643610cbbc268bac35d0bd05577fa 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.616 2008/06/15 01:25:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.617 2008/07/16 01:30:22 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -444,7 +444,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
        UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
        UPDATE USER USING
 
-       VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING
+       VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
        VERBOSE VERSION_P VIEW VOLATILE
 
        WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
@@ -4200,10 +4200,11 @@ func_arg:
                ;
 
 /* INOUT is SQL99 standard, IN OUT is for Oracle compatibility */
-arg_class:     IN_P                                                                    { $$ = FUNC_PARAM_IN; }
-                       | OUT_P                                                                 { $$ = FUNC_PARAM_OUT; }
-                       | INOUT                                                                 { $$ = FUNC_PARAM_INOUT; }
-                       | IN_P OUT_P                                                    { $$ = FUNC_PARAM_INOUT; }
+arg_class:     IN_P                                                            { $$ = FUNC_PARAM_IN; }
+                       | OUT_P                                                         { $$ = FUNC_PARAM_OUT; }
+                       | INOUT                                                         { $$ = FUNC_PARAM_INOUT; }
+                       | IN_P OUT_P                                            { $$ = FUNC_PARAM_INOUT; }
+                       | VARIADIC                                                      { $$ = FUNC_PARAM_VARIADIC; }
                ;
 
 /*
@@ -7336,6 +7337,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($5, $1);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @2;
                                        $$ = (Node *) n;
                                }
@@ -7394,6 +7396,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($3, $5);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @4;
                                        $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n, @2);
                                }
@@ -7406,6 +7409,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($4, $6);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @5;
                                        $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n, @2);
                                }
@@ -7418,6 +7422,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($3, $5);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @4;
                                        $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n, @2);
                                }
@@ -7430,6 +7435,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($4, $6);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @5;
                                        $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n, @2);
                                }
@@ -7441,6 +7447,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($4, makeNullAConst());
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @2;
                                        $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2);
                                }
@@ -7451,6 +7458,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($4, $6);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @5;
                                        $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2);
                                }
@@ -7461,6 +7469,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($5, makeNullAConst());
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @5;
                                        $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2);
                                }
@@ -7471,6 +7480,7 @@ a_expr:           c_expr                                                                  { $$ = $1; }
                                        n->args = list_make2($5, $7);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @6;
                                        $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2);
                                }
@@ -7862,6 +7872,7 @@ func_expr:        func_name '(' ')'
                                        n->args = NIL;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -7872,6 +7883,29 @@ func_expr:       func_name '(' ')'
                                        n->args = $3;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
+                                       n->location = @1;
+                                       $$ = (Node *)n;
+                               }
+                       | func_name '(' VARIADIC a_expr ')'
+                               {
+                                       FuncCall *n = makeNode(FuncCall);
+                                       n->funcname = $1;
+                                       n->args = list_make1($4);
+                                       n->agg_star = FALSE;
+                                       n->agg_distinct = FALSE;
+                                       n->func_variadic = TRUE;
+                                       n->location = @1;
+                                       $$ = (Node *)n;
+                               }
+                       | func_name '(' expr_list ',' VARIADIC a_expr ')'
+                               {
+                                       FuncCall *n = makeNode(FuncCall);
+                                       n->funcname = $1;
+                                       n->args = lappend($3, $6);
+                                       n->agg_star = FALSE;
+                                       n->agg_distinct = FALSE;
+                                       n->func_variadic = TRUE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -7886,6 +7920,7 @@ func_expr:        func_name '(' ')'
                                         * "must be an aggregate", but there's no provision
                                         * for that in FuncCall at the moment.
                                         */
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -7896,6 +7931,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $4;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = TRUE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -7916,6 +7952,7 @@ func_expr:        func_name '(' ')'
                                        n->args = NIL;
                                        n->agg_star = TRUE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -7974,6 +8011,7 @@ func_expr:        func_name '(' ')'
                                        n->args = NIL;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8043,6 +8081,7 @@ func_expr:        func_name '(' ')'
                                        n->args = NIL;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8053,6 +8092,7 @@ func_expr:        func_name '(' ')'
                                        n->args = NIL;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8063,6 +8103,7 @@ func_expr:        func_name '(' ')'
                                        n->args = NIL;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8073,6 +8114,7 @@ func_expr:        func_name '(' ')'
                                        n->args = NIL;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8085,6 +8127,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $3;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8100,6 +8143,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $3;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8111,6 +8155,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $3;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8124,6 +8169,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $3;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8143,6 +8189,7 @@ func_expr:        func_name '(' ')'
                                        n->args = list_make1($3);
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8156,6 +8203,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $4;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8166,6 +8214,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $4;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8176,6 +8225,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $4;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -8186,6 +8236,7 @@ func_expr:        func_name '(' ')'
                                        n->args = $3;
                                        n->agg_star = FALSE;
                                        n->agg_distinct = FALSE;
+                                       n->func_variadic = FALSE;
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
@@ -9362,6 +9413,7 @@ reserved_keyword:
                        | UNIQUE
                        | USER
                        | USING
+                       | VARIADIC
                        | WHEN
                        | WHERE
                ;
@@ -9566,6 +9618,7 @@ makeOverlaps(List *largs, List *rargs, int location)
        n->args = list_concat(largs, rargs);
        n->agg_star = FALSE;
        n->agg_distinct = FALSE;
+       n->func_variadic = FALSE;
        n->location = location;
        return n;
 }
@@ -9625,7 +9678,7 @@ extractArgTypes(List *parameters)
        {
                FunctionParameter *p = (FunctionParameter *) lfirst(i);
 
-               if (p->mode != FUNC_PARAM_OUT)                  /* keep if IN or INOUT */
+               if (p->mode != FUNC_PARAM_OUT)          /* keep if IN, INOUT, VARIADIC */
                        result = lappend(result, p->argType);
        }
        return result;
index 43013e1e7723d0c273bd11e270780811e24e3506..97fba9c9562e1739f2170083762dedb40dcf1cf5 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.198 2008/07/03 20:58:46 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.199 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -393,6 +393,7 @@ const ScanKeyword ScanKeywords[] = {
        {"value", VALUE_P, UNRESERVED_KEYWORD},
        {"values", VALUES, COL_NAME_KEYWORD},
        {"varchar", VARCHAR, COL_NAME_KEYWORD},
+       {"variadic", VARIADIC, RESERVED_KEYWORD},
        {"varying", VARYING, UNRESERVED_KEYWORD},
        {"verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD},
        {"version", VERSION_P, UNRESERVED_KEYWORD},
index 362108ba3fa054188c55e7ea2e1069b72095e468..8addb53e51ee5ac765067cb9619f96b46d4f5120 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.228 2008/04/29 14:59:16 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.229 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -358,8 +358,8 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
                        result = ParseFuncOrColumn(pstate,
                                                                           list_make1(n),
                                                                           list_make1(result),
-                                                                          false, false, true,
-                                                                          -1);
+                                                                          false, false, false,
+                                                                          true, -1);
                }
        }
        /* process trailing subscripts, if any */
@@ -481,8 +481,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                        node = ParseFuncOrColumn(pstate,
                                                                                         list_make1(makeString(name2)),
                                                                                         list_make1(node),
-                                                                                        false, false, true,
-                                                                                        cref->location);
+                                                                                        false, false, false,
+                                                                                        true, cref->location);
                                }
                                break;
                        }
@@ -511,8 +511,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                        node = ParseFuncOrColumn(pstate,
                                                                                         list_make1(makeString(name3)),
                                                                                         list_make1(node),
-                                                                                        false, false, true,
-                                                                                        cref->location);
+                                                                                        false, false, false,
+                                                                                        true, cref->location);
                                }
                                break;
                        }
@@ -552,8 +552,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                        node = ParseFuncOrColumn(pstate,
                                                                                         list_make1(makeString(name4)),
                                                                                         list_make1(node),
-                                                                                        false, false, true,
-                                                                                        cref->location);
+                                                                                        false, false, false,
+                                                                                        true, cref->location);
                                }
                                break;
                        }
@@ -1018,25 +1018,21 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
        List       *targs;
        ListCell   *args;
 
-       /*
-        * Transform the list of arguments.  We use a shallow list copy and then
-        * transform-in-place to avoid O(N^2) behavior from repeated lappend's.
-        *
-        * XXX: repeated lappend() would no longer result in O(n^2) behavior;
-        * worth reconsidering this design?
-        */
-       targs = list_copy(fn->args);
-       foreach(args, targs)
+       /* Transform the list of arguments ... */
+       targs = NIL;
+       foreach(args, fn->args)
        {
-               lfirst(args) = transformExpr(pstate,
-                                                                        (Node *) lfirst(args));
+               targs = lappend(targs, transformExpr(pstate,
+                                                                                        (Node *) lfirst(args)));
        }
 
+       /* ... and hand off to ParseFuncOrColumn */
        return ParseFuncOrColumn(pstate,
                                                         fn->funcname,
                                                         targs,
                                                         fn->agg_star,
                                                         fn->agg_distinct,
+                                                        fn->func_variadic,
                                                         false,
                                                         fn->location);
 }
index 3d44c2520beaf23b9fdf2f9b224d87d5ccb1771f..3bb5c452a8ea629f6602077832eaf46773e0ec61 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.202 2008/03/26 21:10:38 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.203 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,14 +56,14 @@ static void unknown_attribute(ParseState *pstate, Node *relref, char *attname,
  *     intended to be used only to deliver an appropriate error message,
  *     not to affect the semantics.  When is_column is true, we should have
  *     a single argument (the putative table), unqualified function name
- *     equal to the column name, and no aggregate decoration.
+ *     equal to the column name, and no aggregate or variadic decoration.
  *
  *     The argument expressions (in fargs) must have been transformed already.
  */
 Node *
 ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
-                                 bool agg_star, bool agg_distinct, bool is_column,
-                                 int location)
+                                 bool agg_star, bool agg_distinct, bool func_variadic,
+                                 bool is_column, int location)
 {
        Oid                     rettype;
        Oid                     funcid;
@@ -75,6 +75,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        Oid                *declared_arg_types;
        Node       *retval;
        bool            retset;
+       int                     nvargs;
        FuncDetailCode fdresult;
 
        /*
@@ -126,9 +127,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
         * Check for column projection: if function has one argument, and that
         * argument is of complex type, and function name is not qualified, then
         * the "function call" could be a projection.  We also check that there
-        * wasn't any aggregate decoration.
+        * wasn't any aggregate or variadic decoration.
         */
-       if (nargs == 1 && !agg_star && !agg_distinct && list_length(funcname) == 1)
+       if (nargs == 1 && !agg_star && !agg_distinct && !func_variadic &&
+               list_length(funcname) == 1)
        {
                Oid                     argtype = actual_arg_types[0];
 
@@ -153,11 +155,15 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
         * func_get_detail looks up the function in the catalogs, does
         * disambiguation for polymorphic functions, handles inheritance, and
         * returns the funcid and type and set or singleton status of the
-        * function's return value.  it also returns the true argument types to
-        * the function.
+        * function's return value.  It also returns the true argument types to
+        * the function.  (In the case of a variadic function call, the reported
+        * "true" types aren't really what is in pg_proc: the variadic argument is
+        * replaced by a suitable number of copies of its element type.  We'll fix
+        * it up below.)
         */
        fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
-                                                          &funcid, &rettype, &retset,
+                                                          !func_variadic,
+                                                          &funcid, &rettype, &retset, &nvargs,
                                                           &declared_arg_types);
        if (fdresult == FUNCDETAIL_COERCION)
        {
@@ -242,6 +248,34 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        /* perform the necessary typecasting of arguments */
        make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
 
+       /*
+        * If it's a variadic function call, transform the last nvargs arguments
+        * into an array --- unless it's an "any" variadic.
+        */
+       if (nvargs > 0 && declared_arg_types[nargs - 1] != ANYOID)
+       {
+               ArrayExpr *newa = makeNode(ArrayExpr);
+               int     non_var_args = nargs - nvargs;
+               List    *vargs;
+
+               Assert(non_var_args >= 0);
+               vargs = list_copy_tail(fargs, non_var_args);
+               fargs = list_truncate(fargs, non_var_args);
+
+               newa->elements = vargs;
+               /* assume all the variadic arguments were coerced to the same type */
+               newa->element_typeid = exprType((Node *) linitial(vargs));
+               newa->array_typeid = get_array_type(newa->element_typeid);
+               if (!OidIsValid(newa->array_typeid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                        errmsg("could not find array type for data type %s",
+                                                       format_type_be(newa->element_typeid))));
+               newa->multidims = false;
+
+               fargs = lappend(fargs, newa);
+       }
+
        /* build the appropriate output structure */
        if (fdresult == FUNCDETAIL_NORMAL)
        {
@@ -668,21 +702,12 @@ func_select_candidate(int nargs,
  * Find the named function in the system catalogs.
  *
  * Attempt to find the named function in the system catalogs with
- *     arguments exactly as specified, so that the normal case
- *     (exact match) is as quick as possible.
+ * arguments exactly as specified, so that the normal case (exact match)
+ * is as quick as possible.
  *
  * If an exact match isn't found:
  *     1) check for possible interpretation as a type coercion request
- *     2) get a vector of all possible input arg type arrays constructed
- *        from the superclasses of the original input arg types
- *     3) get a list of all possible argument type arrays to the function
- *        with given name and number of arguments
- *     4) for each input arg type array from vector #1:
- *      a) find how many of the function arg type arrays from list #2
- *             it can be coerced to
- *      b) if the answer is one, we have our function
- *      c) if the answer is more than one, attempt to resolve the conflict
- *      d) if the answer is zero, try the next array from vector #1
+ *     2) apply the ambiguous-function resolution rules
  *
  * Note: we rely primarily on nargs/argtypes as the argument description.
  * The actual expression node list is passed in fargs so that we can check
@@ -694,16 +719,18 @@ func_get_detail(List *funcname,
                                List *fargs,
                                int nargs,
                                Oid *argtypes,
+                               bool expand_variadic,
                                Oid *funcid,    /* return value */
                                Oid *rettype,   /* return value */
                                bool *retset,   /* return value */
+                               int *nvargs,    /* return value */
                                Oid **true_typeids)             /* return value */
 {
        FuncCandidateList raw_candidates;
        FuncCandidateList best_candidate;
 
        /* Get list of possible candidates from namespace search */
-       raw_candidates = FuncnameGetCandidates(funcname, nargs);
+       raw_candidates = FuncnameGetCandidates(funcname, nargs, expand_variadic);
 
        /*
         * Quickly check if there is an exact match to the input datatypes (there
@@ -786,6 +813,7 @@ func_get_detail(List *funcname,
                                        *funcid = InvalidOid;
                                        *rettype = targetType;
                                        *retset = false;
+                                       *nvargs = 0;
                                        *true_typeids = argtypes;
                                        return FUNCDETAIL_COERCION;
                                }
@@ -835,6 +863,7 @@ func_get_detail(List *funcname,
                FuncDetailCode result;
 
                *funcid = best_candidate->oid;
+               *nvargs = best_candidate->nvargs;
                *true_typeids = best_candidate->args;
 
                ftup = SearchSysCache(PROCOID,
@@ -1189,7 +1218,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 {
        FuncCandidateList clist;
 
-       clist = FuncnameGetCandidates(funcname, nargs);
+       clist = FuncnameGetCandidates(funcname, nargs, false);
 
        while (clist)
        {
index 33f5b8015f6040f5fc5238a51d14bcc667e58d64..bbdea4642cc10f64c7a2b5ef87a5a2e077ee2eaf 100644 (file)
@@ -19,7 +19,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.13 2008/04/29 14:59:17 alvherre Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.14 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -388,6 +388,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
                funccallnode->args = list_make1(castnode);
                funccallnode->agg_star = false;
                funccallnode->agg_distinct = false;
+               funccallnode->func_variadic = false;
                funccallnode->location = -1;
 
                constraint = makeNode(Constraint);
index 986bac041d927023c507ec5a8a2c5df3215d65be..d50dc23d7783f4e870ff9f98cdfe420ed1a2a15c 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.107 2008/06/19 00:46:05 alvherre Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.108 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -131,7 +131,7 @@ regprocin(PG_FUNCTION_ARGS)
         * pg_proc entries in the current search path.
         */
        names = stringToQualifiedNameList(pro_name_or_oid);
-       clist = FuncnameGetCandidates(names, -1);
+       clist = FuncnameGetCandidates(names, -1, false);
 
        if (clist == NULL)
                ereport(ERROR,
@@ -189,7 +189,8 @@ regprocout(PG_FUNCTION_ARGS)
                         * Would this proc be found (uniquely!) by regprocin? If not,
                         * qualify it.
                         */
-                       clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1);
+                       clist = FuncnameGetCandidates(list_make1(makeString(proname)),
+                                                                                 -1, false);
                        if (clist != NULL && clist->next == NULL &&
                                clist->oid == proid)
                                nspname = NULL;
@@ -276,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS)
         */
        parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
 
-       clist = FuncnameGetCandidates(names, nargs);
+       clist = FuncnameGetCandidates(names, nargs, false);
 
        for (; clist; clist = clist->next)
        {
index 3754b4981edbd2646841346cf4cbe476b097909a..dc4a6cc4a8f853f0ab6fdfc0a5d3ec1f0ee98081 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.275 2008/06/06 17:59:29 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.276 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -193,7 +193,8 @@ static Node *processIndirection(Node *node, deparse_context *context,
                                   bool printit);
 static void printSubscripts(ArrayRef *aref, deparse_context *context);
 static char *generate_relation_name(Oid relid);
-static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
+static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes,
+                                                                       bool *is_variadic);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
@@ -531,7 +532,7 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
                appendStringInfo(&buf, "FOR EACH STATEMENT ");
 
        appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
-                                        generate_function_name(trigrec->tgfoid, 0, NULL));
+                                        generate_function_name(trigrec->tgfoid, 0, NULL, NULL));
 
        if (trigrec->tgnargs > 0)
        {
@@ -4293,6 +4294,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
        Oid                     funcoid = expr->funcid;
        Oid                     argtypes[FUNC_MAX_ARGS];
        int                     nargs;
+       bool            is_variadic;
        ListCell   *l;
 
        /*
@@ -4343,8 +4345,17 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
        }
 
        appendStringInfo(buf, "%s(",
-                                        generate_function_name(funcoid, nargs, argtypes));
-       get_rule_expr((Node *) expr->args, context, true);
+                                        generate_function_name(funcoid, nargs, argtypes,
+                                                                                       &is_variadic));
+       nargs = 0;
+       foreach(l, expr->args)
+       {
+               if (nargs++ > 0)
+                       appendStringInfoString(buf, ", ");
+               if (is_variadic && lnext(l) == NULL)
+                       appendStringInfoString(buf, "VARIADIC ");
+               get_rule_expr((Node *) lfirst(l), context, true);
+       }
        appendStringInfoChar(buf, ')');
 }
 
@@ -4371,7 +4382,8 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
        }
 
        appendStringInfo(buf, "%s(%s",
-                                  generate_function_name(aggref->aggfnoid, nargs, argtypes),
+                                        generate_function_name(aggref->aggfnoid,
+                                                                                       nargs, argtypes, NULL),
                                         aggref->aggdistinct ? "DISTINCT " : "");
        /* aggstar can be set only in zero-argument aggregates */
        if (aggref->aggstar)
@@ -5329,10 +5341,12 @@ generate_relation_name(Oid relid)
  *             given that it is being called with the specified actual arg types.
  *             (Arg types matter because of ambiguous-function resolution rules.)
  *
- * The result includes all necessary quoting and schema-prefixing.
+ * The result includes all necessary quoting and schema-prefixing.  We can
+ * also pass back an indication of whether the function is variadic.
  */
 static char *
-generate_function_name(Oid funcid, int nargs, Oid *argtypes)
+generate_function_name(Oid funcid, int nargs, Oid *argtypes,
+                                          bool *is_variadic)
 {
        HeapTuple       proctup;
        Form_pg_proc procform;
@@ -5343,6 +5357,7 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes)
        Oid                     p_funcid;
        Oid                     p_rettype;
        bool            p_retset;
+       int                     p_nvargs;
        Oid                *p_true_typeids;
 
        proctup = SearchSysCache(PROCOID,
@@ -5352,7 +5367,7 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes)
                elog(ERROR, "cache lookup failed for function %u", funcid);
        procform = (Form_pg_proc) GETSTRUCT(proctup);
        proname = NameStr(procform->proname);
-       Assert(nargs == procform->pronargs);
+       Assert(nargs >= procform->pronargs);
 
        /*
         * The idea here is to schema-qualify only if the parser would fail to
@@ -5360,9 +5375,9 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes)
         * specified argtypes.
         */
        p_result = func_get_detail(list_make1(makeString(proname)),
-                                                          NIL, nargs, argtypes,
+                                                          NIL, nargs, argtypes, false,
                                                           &p_funcid, &p_rettype,
-                                                          &p_retset, &p_true_typeids);
+                                                          &p_retset, &p_nvargs, &p_true_typeids);
        if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) &&
                p_funcid == funcid)
                nspname = NULL;
@@ -5371,6 +5386,34 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes)
 
        result = quote_qualified_identifier(nspname, proname);
 
+       /* Check variadic-ness if caller cares */
+       if (is_variadic)
+       {
+               /* XXX change this if we simplify code in FuncnameGetCandidates */
+               Datum           proargmodes;
+               bool            isnull;
+
+               *is_variadic = false;
+
+               proargmodes = SysCacheGetAttr(PROCOID, proctup,
+                                                                         Anum_pg_proc_proargmodes, &isnull);
+               if (!isnull)
+               {
+                       ArrayType       *ar = DatumGetArrayTypeP(proargmodes);
+                       char            *argmodes;
+                       int     j;
+
+                       argmodes = ARR_DATA_PTR(ar);
+                       j = ARR_DIMS(ar)[0] - 1;
+                       if (j >= 0 && argmodes[j] == PROARGMODE_VARIADIC)
+                       {
+                               /* "any" variadics are not treated as variadics for listing */
+                               if (procform->proargtypes.values[j] != ANYOID)
+                                       *is_variadic = true;
+                       }
+               }
+       }
+
        ReleaseSysCache(proctup);
 
        return result;
index 42586f13aa16cafccb6bd74f94a4f69616f1f41f..7cba375ee038284382abd1fe3f2ccdef4a52c416 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 2002-2008, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.39 2008/03/25 22:42:45 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.40 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -844,7 +844,8 @@ get_func_result_name(Oid functionId)
                numoutargs = 0;
                for (i = 0; i < numargs; i++)
                {
-                       if (argmodes[i] == PROARGMODE_IN)
+                       if (argmodes[i] == PROARGMODE_IN ||
+                               argmodes[i] == PROARGMODE_VARIADIC)
                                continue;
                        Assert(argmodes[i] == PROARGMODE_OUT ||
                                   argmodes[i] == PROARGMODE_INOUT);
@@ -994,7 +995,8 @@ build_function_result_tupdesc_d(Datum proallargtypes,
        {
                char       *pname;
 
-               if (argmodes[i] == PROARGMODE_IN)
+               if (argmodes[i] == PROARGMODE_IN ||
+                       argmodes[i] == PROARGMODE_VARIADIC)
                        continue;
                Assert(argmodes[i] == PROARGMODE_OUT ||
                           argmodes[i] == PROARGMODE_INOUT);
index 88ed75fd50563c4842e3d4d17b8ecbec602376ff..c22a5be9d3922ce30c057981a70fe6888bded051 100644 (file)
@@ -12,7 +12,7 @@
  *     by PostgreSQL
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.493 2008/07/01 11:46:48 heikki Exp $
+ *       $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.494 2008/07/16 01:30:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -6435,15 +6435,18 @@ format_function_arguments(FuncInfo *finfo, int nallargs,
                {
                        switch (argmodes[j][0])
                        {
-                               case 'i':
+                               case PROARGMODE_IN:
                                        argmode = "";
                                        break;
-                               case 'o':
+                               case PROARGMODE_OUT:
                                        argmode = "OUT ";
                                        break;
-                               case 'b':
+                               case PROARGMODE_INOUT:
                                        argmode = "INOUT ";
                                        break;
+                               case PROARGMODE_VARIADIC:
+                                       argmode = "VARIADIC ";
+                                       break;
                                default:
                                        write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
                                        argmode = "";
index 8eb64f38ee2f104a232ae202f3f99773c60104c3..f8f6a657d213f953a8aa4cbcf45fdbfc1d365f5c 100644 (file)
@@ -8,7 +8,7 @@
  *
  * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.181 2008/07/15 16:06:06 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.182 2008/07/16 01:30:23 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -204,7 +204,8 @@ describeFunctions(const char *pattern, bool verbose)
                                          "        CASE\n"
                                          "          WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
                                          "          WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n"
-                                       "          WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
+                                         "          WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
+                                         "          WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n"
                                          "        END ||\n"
                                          "        CASE\n"
                         "          WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n"
index 07f66e80a7f464068691a4b8e14a348db9954cbe..b67d5b2208069e575286d743c550c27d2c3915fa 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.468 2008/07/16 00:48:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.469 2008/07/16 01:30:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200807151
+#define CATALOG_VERSION_NO     200807152
 
 #endif
index b161b7108b6452f2566c5a61cb7cbb6337bf4fa4..749297eaca661f676b22a10569d98c265c71033f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.54 2008/07/01 02:09:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.55 2008/07/16 01:30:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,7 @@ typedef struct _FuncCandidateList
        int                     pathpos;                /* for internal use of namespace lookup */
        Oid                     oid;                    /* the function or operator's OID */
        int                     nargs;                  /* number of arg types returned */
+       int                     nvargs;                 /* number of args to become variadic array */
        Oid                     args[1];                /* arg types --- VARIABLE LENGTH ARRAY */
 }      *FuncCandidateList;     /* VARIABLE LENGTH STRUCT */
 
@@ -51,7 +52,8 @@ extern bool RelationIsVisible(Oid relid);
 extern Oid     TypenameGetTypid(const char *typname);
 extern bool TypeIsVisible(Oid typid);
 
-extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs);
+extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
+                                                                                          bool expand_variadic);
 extern bool FunctionIsVisible(Oid funcid);
 
 extern Oid     OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
index 63f1cc10d2ef14dc76d73259810ede31efa31572..8256ca5ba9da826ac298529cc0f4fb1498636e21 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.506 2008/07/16 00:48:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.507 2008/07/16 01:30:23 tgl Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -4474,5 +4474,6 @@ DESCR("is txid visible in snapshot?");
 #define PROARGMODE_IN          'i'
 #define PROARGMODE_OUT         'o'
 #define PROARGMODE_INOUT       'b'
+#define PROARGMODE_VARIADIC    'v'
 
 #endif   /* PG_PROC_H */
index 472c07b50816a0e4b72b204d28b4e8637bf5901d..561cc9129dba3027f3362d7c4b9c8995b73cb1d0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.366 2008/05/16 23:36:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.367 2008/07/16 01:30:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -261,6 +261,7 @@ typedef struct FuncCall
        List       *args;                       /* the arguments (list of exprs) */
        bool            agg_star;               /* argument was really '*' */
        bool            agg_distinct;   /* arguments were labeled DISTINCT */
+       bool            func_variadic;  /* last argument was labeled VARIADIC */
        int                     location;               /* token location, or -1 if unknown */
 } FuncCall;
 
@@ -1568,7 +1569,8 @@ typedef enum FunctionParameterMode
        /* the assigned enum values appear in pg_proc, don't change 'em! */
        FUNC_PARAM_IN = 'i',            /* input only */
        FUNC_PARAM_OUT = 'o',           /* output only */
-       FUNC_PARAM_INOUT = 'b'          /* both */
+       FUNC_PARAM_INOUT = 'b',         /* both */
+       FUNC_PARAM_VARIADIC = 'v'       /* variadic */
 } FunctionParameterMode;
 
 typedef struct FunctionParameter
index 3635f2eede4fafb8f5a4f1e11c379971d607dbea..ec619a4ac544b48592c68533cc2184b15d7b93bd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.59 2008/01/01 19:45:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.60 2008/07/16 01:30:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,13 +43,13 @@ typedef enum
 
 extern Node *ParseFuncOrColumn(ParseState *pstate,
                                  List *funcname, List *fargs,
-                                 bool agg_star, bool agg_distinct, bool is_column,
-                                 int location);
+                                 bool agg_star, bool agg_distinct, bool func_variadic,
+                                 bool is_column, int location);
 
 extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
-                               int nargs, Oid *argtypes,
+                               int nargs, Oid *argtypes, bool expand_variadic,
                                Oid *funcid, Oid *rettype,
-                               bool *retset, Oid **true_typeids);
+                               bool *retset, int *nvargs, Oid **true_typeids);
 
 extern int func_match_argtypes(int nargs,
                                        Oid *input_typeids,
index 56e38c3497b906678d8c3930de63d9f6e8b4d409..73ad1a577b862c637592de5567b7204e6529932c 100644 (file)
@@ -1,4 +1,4 @@
-/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.368 2008/06/26 08:04:05 meskes Exp $ */
+/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.369 2008/07/16 01:30:23 tgl Exp $ */
 
 /* Copyright comment */
 %{
@@ -489,7 +489,7 @@ add_typedef(char *name, char * dimension, char * length, enum ECPGttype type_enu
        UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
        UPDATE USER USING
 
-       VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING
+       VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
        VERBOSE VERSION_P VIEW VOLATILE
        WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
 
@@ -2629,6 +2629,7 @@ arg_class:  IN_P          { $$ = make_str("in"); }
                | OUT_P         { $$ = make_str("out"); }
                | INOUT         { $$ = make_str("inout"); }
                | IN_P OUT_P    { $$ = make_str("in out"); }
+               | VARIADIC      { $$ = make_str("variadic"); }
                ;
 
 func_as: StringConst
@@ -6857,6 +6858,7 @@ reserved_keyword:
                | UNIQUE                        { $$ = make_str("unique"); }
                | USER                          { $$ = make_str("user"); }
                | USING                         { $$ = make_str("using"); }
+               | VARIADIC                      { $$ = make_str("variadic"); }
                | WHEN                          { $$ = make_str("when"); }
                | WHERE                         { $$ = make_str("where"); }
                ;
index 737bac5888119ab1d4adc1f303120fabcda6c76f..1b2cba38815d17d14190c13069f279eed6fb6e07 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.126 2008/05/13 22:10:29 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.127 2008/07/16 01:30:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -426,7 +426,8 @@ do_compile(FunctionCallInfo fcinfo,
                                {
                                        argitemtype = PLPGSQL_NSTYPE_VAR;
                                        /* input argument vars are forced to be CONSTANT */
-                                       if (argmode == PROARGMODE_IN)
+                                       if (argmode == PROARGMODE_IN ||
+                                               argmode == PROARGMODE_VARIADIC)
                                                ((PLpgSQL_var *) argvariable)->isconst = true;
                                }
                                else
@@ -436,7 +437,9 @@ do_compile(FunctionCallInfo fcinfo,
                                }
 
                                /* Remember arguments in appropriate arrays */
-                               if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT)
+                               if (argmode == PROARGMODE_IN ||
+                                       argmode == PROARGMODE_INOUT ||
+                                       argmode == PROARGMODE_VARIADIC)
                                        in_arg_varnos[num_in_args++] = argvariable->dno;
                                if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT)
                                        out_arg_variables[num_out_args++] = argvariable;
index a2da9a6dcee2606de3b2cdadb8e3d375407c040d..8e85c6707e2d044817d15a1da2d31e6296cea3b8 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.110 2008/05/12 00:00:54 alvherre Exp $
+ *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.111 2008/07/16 01:30:23 tgl Exp $
  *
  *********************************************************************
  */
@@ -1271,7 +1271,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
                                /* proc->nargs was initialized to 0 above */
                                for (i = 0; i < total; i++)
                                {
-                                       if (modes[i] != 'o')
+                                       if (modes[i] != PROARGMODE_OUT)
                                                (proc->nargs)++;
                                }
                        }
@@ -1282,8 +1282,8 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
                                HeapTuple       argTypeTup;
                                Form_pg_type argTypeStruct;
 
-                               if (modes && modes[i] == 'o')   /* skip OUT arguments */
-                                       continue;
+                               if (modes && modes[i] == PROARGMODE_OUT)
+                                       continue;       /* skip OUT arguments */
 
                                Assert(types[i] == procStruct->proargtypes.values[pos]);
 
index 582c06785a8aee50c04bcbd95756676da4ff9335..1e4ef2645fa801e82a5747b7ac0ffdce3fa4ec87 100644 (file)
@@ -3544,3 +3544,93 @@ select case_test(13);
 
 drop function catch();
 drop function case_test(bigint);
+-- test variadic functions
+create or replace function vari(variadic int[])
+returns void as $$
+begin
+  for i in array_lower($1,1)..array_upper($1,1) loop
+    raise notice '%', $1[i];
+  end loop; end;
+$$ language plpgsql;
+select vari(1,2,3,4,5);
+NOTICE:  1
+NOTICE:  2
+NOTICE:  3
+NOTICE:  4
+NOTICE:  5
+ vari 
+------
+(1 row)
+
+select vari(3,4,5);
+NOTICE:  3
+NOTICE:  4
+NOTICE:  5
+ vari 
+------
+(1 row)
+
+select vari(variadic array[5,6,7]);
+NOTICE:  5
+NOTICE:  6
+NOTICE:  7
+ vari 
+------
+(1 row)
+
+drop function vari(int[]);
+-- coercion test
+create or replace function pleast(variadic numeric[])
+returns numeric as $$
+declare aux numeric = $1[array_lower($1,1)];
+begin
+  for i in array_lower($1,1)+1..array_upper($1,1) loop
+    if $1[i] < aux then aux := $1[i]; end if;
+  end loop;
+  return aux;
+end;
+$$ language plpgsql immutable strict;
+select pleast(10,1,2,3,-16);
+ pleast 
+--------
+    -16
+(1 row)
+
+select pleast(10.2,2.2,-1.1);
+ pleast 
+--------
+   -1.1
+(1 row)
+
+select pleast(10.2,10, -20);
+ pleast 
+--------
+    -20
+(1 row)
+
+select pleast(10,20, -1.0);
+ pleast 
+--------
+   -1.0
+(1 row)
+
+-- in case of conflict, non-variadic version is preferred
+create or replace function pleast(numeric)
+returns numeric as $$
+begin
+  raise notice 'non-variadic function called';
+  return $1;
+end;
+$$ language plpgsql immutable strict;
+select pleast(10);
+NOTICE:  non-variadic function called
+ pleast 
+--------
+     10
+(1 row)
+
+drop function pleast(numeric[]);
+drop function pleast(numeric);
index a208203c6d39881a911919f9a221ce5c0e96a7b0..3779f8e58ce20f52e324d70acc9f1d7e0a99891a 100644 (file)
@@ -613,3 +613,111 @@ create aggregate build_group(int8, integer) (
   SFUNC = add_group,
   STYPE = int8[]
 );
+-- test variadic polymorphic functions
+create function myleast(variadic anyarray) returns anyelement as $$
+  select min($1[i]) from generate_subscripts($1,1) g(i)
+$$ language sql immutable strict;
+select myleast(10, 1, 20, 33);
+ myleast 
+---------
+       1
+(1 row)
+
+select myleast(1.1, 0.22, 0.55);
+ myleast 
+---------
+    0.22
+(1 row)
+
+select myleast('z'::text);
+ myleast 
+---------
+ z
+(1 row)
+
+select myleast(); -- fail
+ERROR:  function myleast() does not exist
+LINE 1: select myleast();
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+-- test with variadic call parameter
+select myleast(variadic array[1,2,3,4,-1]);
+ myleast 
+---------
+      -1
+(1 row)
+
+select myleast(variadic array[1.1, -5.5]);
+ myleast 
+---------
+    -5.5
+(1 row)
+
+--test with empty variadic call parameter
+select myleast(variadic array[]::int[]);
+ myleast 
+---------
+        
+(1 row)
+
+-- an example with some ordinary arguments too
+create function concat(text, variadic anyarray) returns text as $$
+  select array_to_string($2, $1);
+$$ language sql immutable strict;
+select concat('%', 1, 2, 3, 4, 5);
+  concat   
+-----------
+ 1%2%3%4%5
+(1 row)
+
+select concat('|', 'a'::text, 'b', 'c');
+ concat 
+--------
+ a|b|c
+(1 row)
+
+select concat('|', variadic array[1,2,33]);
+ concat 
+--------
+ 1|2|33
+(1 row)
+
+select concat('|', variadic array[]::int[]);
+ concat 
+--------
+(1 row)
+
+drop function concat(text, anyarray);
+-- mix variadic with anyelement
+create function formarray(anyelement, variadic anyarray) returns anyarray as $$
+  select array_prepend($1, $2);
+$$ language sql immutable strict;
+select formarray(1,2,3,4,5);
+  formarray  
+-------------
+ {1,2,3,4,5}
+(1 row)
+
+select formarray(1.1, variadic array[1.2,55.5]);
+   formarray    
+----------------
+ {1.1,1.2,55.5}
+(1 row)
+
+select formarray(1.1, array[1.2,55.5]); -- fail without variadic
+ERROR:  function formarray(numeric, numeric[]) does not exist
+LINE 1: select formarray(1.1, array[1.2,55.5]);
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select formarray(1, 'x'::text); -- fail, type mismatch
+ERROR:  function formarray(integer, text) does not exist
+LINE 1: select formarray(1, 'x'::text);
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select formarray(1, variadic array['x'::text]); -- fail, type mismatch
+ERROR:  function formarray(integer, text[]) does not exist
+LINE 1: select formarray(1, variadic array['x'::text]);
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function formarray(anyelement, variadic anyarray);
index 0267dda30ca21662ee7dbe0b6cf659aab93c01d0..4d45dba2efcbcfdba463467bbd1fdf7093445fb2 100644 (file)
@@ -2878,3 +2878,50 @@ select case_test(13);
 
 drop function catch();
 drop function case_test(bigint);
+
+-- test variadic functions
+
+create or replace function vari(variadic int[])
+returns void as $$
+begin
+  for i in array_lower($1,1)..array_upper($1,1) loop
+    raise notice '%', $1[i];
+  end loop; end;
+$$ language plpgsql;
+
+select vari(1,2,3,4,5);
+select vari(3,4,5);
+select vari(variadic array[5,6,7]);
+
+drop function vari(int[]);
+
+-- coercion test
+create or replace function pleast(variadic numeric[])
+returns numeric as $$
+declare aux numeric = $1[array_lower($1,1)];
+begin
+  for i in array_lower($1,1)+1..array_upper($1,1) loop
+    if $1[i] < aux then aux := $1[i]; end if;
+  end loop;
+  return aux;
+end;
+$$ language plpgsql immutable strict;
+
+select pleast(10,1,2,3,-16);
+select pleast(10.2,2.2,-1.1);
+select pleast(10.2,10, -20);
+select pleast(10,20, -1.0);
+
+-- in case of conflict, non-variadic version is preferred
+create or replace function pleast(numeric)
+returns numeric as $$
+begin
+  raise notice 'non-variadic function called';
+  return $1;
+end;
+$$ language plpgsql immutable strict;
+
+select pleast(10);
+
+drop function pleast(numeric[]);
+drop function pleast(numeric);
index 2df963952f4e2ff9583a5ad63a4520cbb6df3d51..a4e2b2da3e3ae8ce2b24db974cade79e05b3ee47 100644 (file)
@@ -426,3 +426,46 @@ create aggregate build_group(int8, integer) (
   SFUNC = add_group,
   STYPE = int8[]
 );
+
+-- test variadic polymorphic functions
+
+create function myleast(variadic anyarray) returns anyelement as $$
+  select min($1[i]) from generate_subscripts($1,1) g(i)
+$$ language sql immutable strict;
+
+select myleast(10, 1, 20, 33);
+select myleast(1.1, 0.22, 0.55);
+select myleast('z'::text);
+select myleast(); -- fail
+
+-- test with variadic call parameter
+select myleast(variadic array[1,2,3,4,-1]);
+select myleast(variadic array[1.1, -5.5]);
+
+--test with empty variadic call parameter
+select myleast(variadic array[]::int[]);
+
+-- an example with some ordinary arguments too
+create function concat(text, variadic anyarray) returns text as $$
+  select array_to_string($2, $1);
+$$ language sql immutable strict;
+
+select concat('%', 1, 2, 3, 4, 5);
+select concat('|', 'a'::text, 'b', 'c');
+select concat('|', variadic array[1,2,33]);
+select concat('|', variadic array[]::int[]);
+
+drop function concat(text, anyarray);
+
+-- mix variadic with anyelement
+create function formarray(anyelement, variadic anyarray) returns anyarray as $$
+  select array_prepend($1, $2);
+$$ language sql immutable strict;
+
+select formarray(1,2,3,4,5);
+select formarray(1.1, variadic array[1.2,55.5]);
+select formarray(1.1, array[1.2,55.5]); -- fail without variadic
+select formarray(1, 'x'::text); -- fail, type mismatch
+select formarray(1, variadic array['x'::text]); -- fail, type mismatch
+
+drop function formarray(anyelement, variadic anyarray);