so long as all the trailing arguments are of the same (non-array) type.
The function receives them as a single array argument (which is why they
have to all be the same type).
It might be useful to extend this facility to aggregates, but this patch
doesn't do that.
This patch imposes a noticeable slowdown on function lookup --- a follow-on
patch will fix that by adding a redundant column to pg_proc.
Pavel Stehule
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.168 2008/07/14 00:51:45 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.169 2008/07/16 01:30:21 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
<entry>
An array with the data types of the function arguments. This includes
- only input arguments (including <literal>INOUT</literal> arguments), and thus represents
+ only input arguments (including <literal>INOUT</literal> and
+ <literal>VARIADIC</> arguments), and thus represents
the call signature of the function
</entry>
</row>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
<entry>
An array with the data types of the function arguments. This includes
- all arguments (including <literal>OUT</literal> and <literal>INOUT</literal> arguments); however, if all the
- arguments are IN arguments, this field will be null.
+ all arguments (including <literal>OUT</literal> and
+ <literal>INOUT</literal> arguments); however, if all the
+ arguments are <literal>IN</literal> arguments, this field will be null.
Note that subscripting is 1-based, whereas for historical reasons
<structfield>proargtypes</> is subscripted from 0
</entry>
An array with the modes of the function arguments, encoded as
<literal>i</literal> for <literal>IN</> arguments,
<literal>o</literal> for <literal>OUT</> arguments,
- <literal>b</literal> for <literal>INOUT</> arguments.
- If all the arguments are <literal>IN</literal> arguments, this field will be null.
+ <literal>b</literal> for <literal>INOUT</> arguments,
+ <literal>v</literal> for <literal>VARIADIC</> arguments.
+ If all the arguments are <literal>IN</literal> arguments,
+ this field will be null.
Note that subscripts correspond to positions of
<structfield>proallargtypes</> not <structfield>proargtypes</>
</entry>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.36 2007/08/31 21:33:48 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.37 2008/07/16 01:30:21 tgl Exp $ -->
<chapter id="extend">
<title>Extending <acronym>SQL</acronym></title>
is equivalent to declaring it as <literal>f(anyenum, anyenum)</>:
both actual arguments have to be the same enum type.
</para>
+
+ <para>
+ A variadic function (one taking a variable number of arguments, as in
+ <xref linkend="xfunc-sql-variadic-functions">) can be
+ polymorphic: this is accomplished by declaring its last parameter as
+ <literal>VARIADIC</> <type>anyarray</>. For purposes of argument
+ matching and determining the actual result type, such a function behaves
+ the same as if you had written the appropriate number of
+ <type>anynonarray</> parameters.
+ </para>
</sect2>
</sect1>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.131 2008/06/27 01:52:59 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.132 2008/07/16 01:30:21 tgl Exp $ -->
<chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
calling query, as discussed in <xref linkend="queries-tablefunctions">.
</para>
+ <para>
+ <application>PL/pgSQL</> functions can be declared to accept a variable
+ number of arguments by using the <literal>VARIADIC</> marker. This
+ works exactly the same way as for SQL functions, as discussed in
+ <xref linkend="xfunc-sql-variadic-functions">.
+ </para>
+
<para>
<application>PL/pgSQL</> functions can also be declared to accept
and return the polymorphic types
<!--
-$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
-->
<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>
<!--
-$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
-->
<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>
<!--
-$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">
<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>
<!--
-$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
-->
<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>
-<!-- $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>
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>
-<!-- $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>
None
(1 row)
</screen>
- </para>
+ </para>
<para>
Still another way to use a function that returns a composite type is to
</sect2>
<sect2 id="xfunc-output-parameters">
- <title>Functions with Output Parameters</title>
+ <title><acronym>SQL</> Functions with Output Parameters</title>
<indexterm>
<primary>function</primary>
<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 — 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>
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>
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
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>
*
* 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 $
*/
/*
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,
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
* 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);
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))
/* Consider only procs in specified namespace */
if (procform->pronamespace != namespaceId)
continue;
- /* No need to check args, they must all be different */
}
else
{
}
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;
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 */
}
}
/*
* 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;
}
visible = false;
- clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs);
+ clist = FuncnameGetCandidates(list_make1(makeString(proname)),
+ nargs, false);
for (; clist; clist = clist->next)
{
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
Oid fnOid;
bool retset;
+ int nvargs;
Oid *true_oid_array;
FuncDetailCode fdresult;
AclResult aclresult;
* 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 */
*
*
* 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
Datum *paramModes;
Datum *paramNames;
int outCount = 0;
+ int varCount = 0;
bool have_names = false;
ListCell *x;
int i;
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);
/* 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');
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
*
*
* 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*
WRITE_NODE_FIELD(args);
WRITE_BOOL_FIELD(agg_star);
WRITE_BOOL_FIELD(agg_distinct);
+ WRITE_BOOL_FIELD(func_variadic);
WRITE_INT_FIELD(location);
}
*
*
* 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
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
;
/* 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; }
;
/*
n->args = list_make2($5, $1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @2;
$$ = (Node *) n;
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
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;
}
* "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;
}
n->args = $4;
n->agg_star = FALSE;
n->agg_distinct = TRUE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = NIL;
n->agg_star = TRUE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = list_make1($3);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = $4;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = $4;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = $4;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
| UNIQUE
| USER
| USING
+ | VARIADIC
| WHEN
| WHERE
;
n->args = list_concat(largs, rargs);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
+ n->func_variadic = FALSE;
n->location = location;
return n;
}
{
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{"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},
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
result = ParseFuncOrColumn(pstate,
list_make1(n),
list_make1(result),
- false, false, true,
- -1);
+ false, false, false,
+ true, -1);
}
}
/* process trailing subscripts, if any */
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name2)),
list_make1(node),
- false, false, true,
- cref->location);
+ false, false, false,
+ true, cref->location);
}
break;
}
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name3)),
list_make1(node),
- false, false, true,
- cref->location);
+ false, false, false,
+ true, cref->location);
}
break;
}
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name4)),
list_make1(node),
- false, false, true,
- cref->location);
+ false, false, false,
+ true, cref->location);
}
break;
}
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);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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;
Oid *declared_arg_types;
Node *retval;
bool retset;
+ int nvargs;
FuncDetailCode fdresult;
/*
* 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];
* 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)
{
/* 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)
{
* 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
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
*funcid = InvalidOid;
*rettype = targetType;
*retset = false;
+ *nvargs = 0;
*true_typeids = argtypes;
return FUNCDETAIL_COERCION;
}
FuncDetailCode result;
*funcid = best_candidate->oid;
+ *nvargs = best_candidate->nvargs;
*true_typeids = best_candidate->args;
ftup = SearchSysCache(PROCOID,
{
FuncCandidateList clist;
- clist = FuncnameGetCandidates(funcname, nargs);
+ clist = FuncnameGetCandidates(funcname, nargs, false);
while (clist)
{
* 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 $
*
*-------------------------------------------------------------------------
*/
funccallnode->args = list_make1(castnode);
funccallnode->agg_star = false;
funccallnode->agg_distinct = false;
+ funccallnode->func_variadic = false;
funccallnode->location = -1;
constraint = makeNode(Constraint);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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,
* 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;
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
- clist = FuncnameGetCandidates(names, nargs);
+ clist = FuncnameGetCandidates(names, nargs, false);
for (; clist; clist = clist->next)
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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)
{
Oid funcoid = expr->funcid;
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
+ bool is_variadic;
ListCell *l;
/*
}
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, ')');
}
}
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)
* 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;
Oid p_funcid;
Oid p_rettype;
bool p_retset;
+ int p_nvargs;
Oid *p_true_typeids;
proctup = SearchSysCache(PROCOID,
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
* 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;
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
{
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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 = "";
*
* 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"
" 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"
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200807151
+#define CATALOG_VERSION_NO 200807152
#endif
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
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);
* 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
#define PROARGMODE_IN 'i'
#define PROARGMODE_OUT 'o'
#define PROARGMODE_INOUT 'b'
+#define PROARGMODE_VARIADIC 'v'
#endif /* PG_PROC_H */
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
/* 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
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
-/* $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 */
%{
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
| OUT_P { $$ = make_str("out"); }
| INOUT { $$ = make_str("inout"); }
| IN_P OUT_P { $$ = make_str("in out"); }
+ | VARIADIC { $$ = make_str("variadic"); }
;
func_as: StringConst
| UNIQUE { $$ = make_str("unique"); }
| USER { $$ = make_str("user"); }
| USING { $$ = make_str("using"); }
+ | VARIADIC { $$ = make_str("variadic"); }
| WHEN { $$ = make_str("when"); }
| WHERE { $$ = make_str("where"); }
;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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
}
/* 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;
/**********************************************************************
* 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 $
*
*********************************************************************
*/
/* proc->nargs was initialized to 0 above */
for (i = 0; i < total; i++)
{
- if (modes[i] != 'o')
+ if (modes[i] != PROARGMODE_OUT)
(proc->nargs)++;
}
}
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]);
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);
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);
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);
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);