aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/catalogs.sgml16
-rw-r--r--doc/src/sgml/extend.sgml12
-rw-r--r--doc/src/sgml/plpgsql.sgml9
-rw-r--r--doc/src/sgml/ref/alter_function.sgml11
-rw-r--r--doc/src/sgml/ref/comment.sgml11
-rw-r--r--doc/src/sgml/ref/create_function.sgml7
-rw-r--r--doc/src/sgml/ref/drop_function.sgml11
-rw-r--r--doc/src/sgml/typeconv.sgml14
-rw-r--r--doc/src/sgml/xfunc.sgml136
-rw-r--r--src/backend/catalog/information_schema.sql3
-rw-r--r--src/backend/catalog/namespace.c184
-rw-r--r--src/backend/catalog/pg_aggregate.c7
-rw-r--r--src/backend/commands/functioncmds.c33
-rw-r--r--src/backend/nodes/copyfuncs.c3
-rw-r--r--src/backend/nodes/equalfuncs.c3
-rw-r--r--src/backend/nodes/outfuncs.c3
-rw-r--r--src/backend/parser/gram.y67
-rw-r--r--src/backend/parser/keywords.c3
-rw-r--r--src/backend/parser/parse_expr.c36
-rw-r--r--src/backend/parser/parse_func.c75
-rw-r--r--src/backend/parser/parse_utilcmd.c3
-rw-r--r--src/backend/utils/adt/regproc.c9
-rw-r--r--src/backend/utils/adt/ruleutils.c65
-rw-r--r--src/backend/utils/fmgr/funcapi.c8
-rw-r--r--src/bin/pg_dump/pg_dump.c11
-rw-r--r--src/bin/psql/describe.c5
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/namespace.h6
-rw-r--r--src/include/catalog/pg_proc.h3
-rw-r--r--src/include/nodes/parsenodes.h6
-rw-r--r--src/include/parser/parse_func.h10
-rw-r--r--src/interfaces/ecpg/preproc/preproc.y6
-rw-r--r--src/pl/plpgsql/src/pl_comp.c9
-rw-r--r--src/pl/plpython/plpython.c8
-rw-r--r--src/test/regress/expected/plpgsql.out90
-rw-r--r--src/test/regress/expected/polymorphism.out108
-rw-r--r--src/test/regress/sql/plpgsql.sql47
-rw-r--r--src/test/regress/sql/polymorphism.sql43
38 files changed, 915 insertions, 170 deletions
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 76198b0f832..7accea0f76c 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 e2805b41917..caeda7e75ca 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 cd1531999b7..42bd6048b68 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>
@@ -122,6 +122,13 @@
</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
<type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
diff --git a/doc/src/sgml/ref/alter_function.sgml b/doc/src/sgml/ref/alter_function.sgml
index bee2f6f4390..abedfe7d50b 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 c8993e915b8..2da64f87aca 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 8c542982d52..18b9bf7beea 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 bf39f5356dd..256cafe684f 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 451555cd022..4f04801210b 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 efee5d8d469..55ed719ec64 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,7 +861,7 @@ 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
@@ -804,6 +870,38 @@ SELECT * FROM dup(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>
</sect1>
@@ -853,6 +951,16 @@ CREATE FUNCTION test(smallint, double precision) RETURNS ...
</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
overloaded functions must be different from the C names of all
@@ -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 a9566d9d3f3..0e2452fa0f6 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 dfaea41cfe4..48b8ee45e6b 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 1ff7261877f..e1c67ce5cd4 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 9831692eea0..cb249d9c7d1 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 a42c40327f2..2e1ce4cb0d7 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 435ee6a6afb..41999226b6d 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 51b46a83edf..a03063ce1e6 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 4e59e37da91..70bbe940afd 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 43013e1e772..97fba9c9562 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 362108ba3fa..8addb53e51e 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 3d44c2520be..3bb5c452a8e 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 33f5b8015f6..bbdea4642cc 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 986bac041d9..d50dc23d778 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 3754b4981ed..dc4a6cc4a8f 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 42586f13aa1..7cba375ee03 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 88ed75fd505..c22a5be9d39 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 8eb64f38ee2..f8f6a657d21 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 07f66e80a7f..b67d5b22080 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 b161b7108b6..749297eaca6 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 63f1cc10d2e..8256ca5ba9d 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 472c07b5081..561cc9129db 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 3635f2eede4..ec619a4ac54 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 56e38c3497b..73ad1a577b8 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 737bac58881..1b2cba38815 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 a2da9a6dcee..8e85c6707e2 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 582c06785a8..1e4ef2645fa 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 a208203c6d3..3779f8e58ce 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 0267dda30ca..4d45dba2efc 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 2df963952f4..a4e2b2da3e3 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);