diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 76198b0f832ba10c39a49619b8111e952133418b..7accea0f76c721bd2aad3cd7a0f074ac2ef2888b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -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
  -->
@@ -3643,7 +3643,8 @@
       <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>
@@ -3654,8 +3655,9 @@
       <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>
@@ -3669,8 +3671,10 @@
         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>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index e2805b4191753d30efb2227cb13e3213d38f2643..caeda7e75cad27d6d0060ddce35aec033fee785d 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -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>
@@ -254,6 +254,16 @@
      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>
 
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index cd1531999b706ac911159a8020923f2ec4cd283f..42bd6048b68758c4278027b5ba88033136300856 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -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>
@@ -121,6 +121,13 @@
      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
diff --git a/doc/src/sgml/ref/alter_function.sgml b/doc/src/sgml/ref/alter_function.sgml
index bee2f6f4390f201be9b3c7fdc4cfe038bf7bb2eb..abedfe7d50becbb515d33d9dab028d2b22b7c3b3 100644
--- a/doc/src/sgml/ref/alter_function.sgml
+++ b/doc/src/sgml/ref/alter_function.sgml
@@ -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>
diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index c8993e915b8e683f4fc722f7feda32eeb7e6a8ae..2da64f87aca4ab5b9349aeb3a89af430845d4536 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -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>
diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml
index 8c542982d52ba32733dd658ef13bd9394ec021e0..18b9bf7beeac466b43ffa4fbe8b95e2b3129e51f 100644
--- a/doc/src/sgml/ref/create_function.sgml
+++ b/doc/src/sgml/ref/create_function.sgml
@@ -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>
diff --git a/doc/src/sgml/ref/drop_function.sgml b/doc/src/sgml/ref/drop_function.sgml
index bf39f5356ddff9f3b1c1294fcc0364c6707942cb..256cafe684f1e1bb2b28402024dd7cd74f5e1145 100644
--- a/doc/src/sgml/ref/drop_function.sgml
+++ b/doc/src/sgml/ref/drop_function.sgml
@@ -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>
diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml
index 451555cd022708b89f7c025e2a9746eafbe3e52a..4f04801210bdb79a182a551dc3ce948271a6e136 100644
--- a/doc/src/sgml/typeconv.sgml
+++ b/doc/src/sgml/typeconv.sgml
@@ -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>
 
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index efee5d8d46914b57c6a52c1d514940e835e327f7..55ed719ec64c1701f22fbd8d6308a951349b3ff3 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -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>
 
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index a9566d9d3f36b8fe73ce00253038688cbfa2c582..0e2452fa0f6ab7fc3c15ba0c20460c36289cfb6a 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -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,
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index dfaea41cfe4846e213d564d14bf14e84b0c0ce2c..48b8ee45e6b2bdbd10673dc17d5a243b94b5af52 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -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;
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 1ff7261877f1ae74b096a9fda993abd5b290df47..e1c67ce5cd4cfce53263b858ccec33e4f8746547 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -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 */
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 9831692eea0a4fa82b54f2202fdbae63ce23459c..cb249d9c7d1987de4a6f47935d8a9f11b414e524 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -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');
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a42c40327f2b25bdc1ca029eae8a0366328ee584..2e1ce4cb0d7bca52f23bd247d2bbc93cc9659a76 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 435ee6a6afbbe4ac2d5c30c601fcbe8090dabbcd..41999226b6d625a40714a48876c074547d7ea5c9 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 51b46a83edfb6ab3603562f4164a5598e87b85b9..a03063ce1e69f3c71d9640f12023219b38e5c9d8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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);
 }
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4e59e37da91f04cb777705c441571c1b2a800178..70bbe940afd643610cbbc268bac35d0bd05577fa 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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;
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 43013e1e7723d0c273bd11e270780811e24e3506..97fba9c9562e1739f2170083762dedb40dcf1cf5 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -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},
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 362108ba3fa054188c55e7ea2e1069b72095e468..8addb53e51ee5ac765067cb9619f96b46d4f5120 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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);
 }
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 3d44c2520beaf23b9fdf2f9b224d87d5ccb1771f..3bb5c452a8ea629f6602077832eaf46773e0ec61 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -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)
 	{
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 33f5b8015f6040f5fc5238a51d14bcc667e58d64..bbdea4642cc10f64c7a2b5ef87a5a2e077ee2eaf 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -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);
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 986bac041d927023c507ec5a8a2c5df3215d65be..d50dc23d7783f4e870ff9f98cdfe420ed1a2a15c 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -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)
 	{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3754b4981edbd2646841346cf4cbe476b097909a..dc4a6cc4a8f853f0ab6fdfc0a5d3ec1f0ee98081 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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;
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 42586f13aa16cafccb6bd74f94a4f69616f1f41f..7cba375ee038284382abd1fe3f2ccdef4a52c416 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -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);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 88ed75fd50563c4842e3d4d17b8ecbec602376ff..c22a5be9d3922ce30c057981a70fe6888bded051 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -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 = "";
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 8eb64f38ee2f104a232ae202f3f99773c60104c3..f8f6a657d213f953a8aa4cbcf45fdbfc1d365f5c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -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"
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 07f66e80a7f464068691a4b8e14a348db9954cbe..b67d5b2208069e575286d743c550c27d2c3915fa 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -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
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index b161b7108b6452f2566c5a61cb7cbb6337bf4fa4..749297eaca661f676b22a10569d98c265c71033f 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -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);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 63f1cc10d2ef14dc76d73259810ede31efa31572..8256ca5ba9da826ac298529cc0f4fb1498636e21 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -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 */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 472c07b50816a0e4b72b204d28b4e8637bf5901d..561cc9129dba3027f3362d7c4b9c8995b73cb1d0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 3635f2eede4fafb8f5a4f1e11c379971d607dbea..ec619a4ac544b48592c68533cc2184b15d7b93bd 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -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,
diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y
index 56e38c3497b906678d8c3930de63d9f6e8b4d409..73ad1a577b862c637592de5567b7204e6529932c 100644
--- a/src/interfaces/ecpg/preproc/preproc.y
+++ b/src/interfaces/ecpg/preproc/preproc.y
@@ -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"); }
 		;
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 737bac5888119ab1d4adc1f303120fabcda6c76f..1b2cba38815d17d14190c13069f279eed6fb6e07 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -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;
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index a2da9a6dcee2606de3b2cdadb8e3d375407c040d..8e85c6707e2d044817d15a1da2d31e6296cea3b8 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -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]);
 
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 582c06785a8aee50c04bcbd95756676da4ff9335..1e4ef2645fa801e82a5747b7ac0ffdce3fa4ec87 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -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);
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index a208203c6d39881a911919f9a221ce5c0e96a7b0..3779f8e58ce20f52e324d70acc9f1d7e0a99891a 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -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);
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 0267dda30ca21662ee7dbe0b6cf659aab93c01d0..4d45dba2efcbcfdba463467bbd1fdf7093445fb2 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -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);
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index 2df963952f4e2ff9583a5ad63a4520cbb6df3d51..a4e2b2da3e3ae8ce2b24db974cade79e05b3ee47 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -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);