Support "variadic" functions, which can accept a variable number of arguments

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
This commit is contained in:
Tom Lane 2008-07-16 01:30:23 +00:00
parent 2c773296f8
commit d89737d31c
38 changed files with 915 additions and 170 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.168 2008/07/14 00:51:45 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.169 2008/07/16 01:30:21 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
@ -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>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.36 2007/08/31 21:33:48 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.37 2008/07/16 01:30:21 tgl Exp $ -->
<chapter id="extend">
<title>Extending <acronym>SQL</acronym></title>
@ -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>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.131 2008/06/27 01:52:59 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.132 2008/07/16 01:30:21 tgl Exp $ -->
<chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@ -121,6 +121,13 @@
calling query, as discussed in <xref linkend="queries-tablefunctions">.
</para>
<para>
<application>PL/pgSQL</> functions can be declared to accept a variable
number of arguments by using the <literal>VARIADIC</> marker. This
works exactly the same way as for SQL functions, as discussed in
<xref linkend="xfunc-sql-variadic-functions">.
</para>
<para>
<application>PL/pgSQL</> functions can also be declared to accept
and return the polymorphic types

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_function.sgml,v 1.15 2007/09/03 18:46:29 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_function.sgml,v 1.16 2008/07/16 01:30:21 tgl Exp $
PostgreSQL documentation
-->
@ -81,13 +81,14 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
<listitem>
<para>
The mode of an argument: either <literal>IN</>, <literal>OUT</>,
or <literal>INOUT</>. If omitted, the default is <literal>IN</>.
The mode of an argument: <literal>IN</>, <literal>OUT</>,
<literal>INOUT</>, or <literal>VARIADIC</>.
If omitted, the default is <literal>IN</>.
Note that <command>ALTER FUNCTION</command> does not actually pay
any attention to <literal>OUT</> arguments, since only the input
arguments are needed to determine the function's identity.
So it is sufficient to list the <literal>IN</> and <literal>INOUT</>
arguments.
So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
and <literal>VARIADIC</> arguments.
</para>
</listitem>
</varlistentry>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.36 2007/08/21 21:08:47 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.37 2008/07/16 01:30:21 tgl Exp $
PostgreSQL documentation
-->
@ -136,13 +136,14 @@ COMMENT ON
<listitem>
<para>
The mode of a function argument: either <literal>IN</>, <literal>OUT</>,
or <literal>INOUT</>. If omitted, the default is <literal>IN</>.
The mode of a function argument: <literal>IN</>, <literal>OUT</>,
<literal>INOUT</>, or <literal>VARIADIC</>.
If omitted, the default is <literal>IN</>.
Note that <command>COMMENT ON FUNCTION</command> does not actually pay
any attention to <literal>OUT</> arguments, since only the input
arguments are needed to determine the function's identity.
So it is sufficient to list the <literal>IN</> and <literal>INOUT</>
arguments.
So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
and <literal>VARIADIC</> arguments.
</para>
</listitem>
</varlistentry>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.78 2007/09/11 00:06:41 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.79 2008/07/16 01:30:21 tgl Exp $
-->
<refentry id="SQL-CREATEFUNCTION">
@ -101,8 +101,9 @@ CREATE [ OR REPLACE ] FUNCTION
<listitem>
<para>
The mode of an argument: either <literal>IN</>, <literal>OUT</>,
or <literal>INOUT</>. If omitted, the default is <literal>IN</>.
The mode of an argument: <literal>IN</>, <literal>OUT</>,
<literal>INOUT</>, or <literal>VARIADIC</>.
If omitted, the default is <literal>IN</>.
</para>
</listitem>
</varlistentry>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.33 2007/01/31 23:26:03 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.34 2008/07/16 01:30:21 tgl Exp $
PostgreSQL documentation
-->
@ -65,13 +65,14 @@ DROP FUNCTION [ IF EXISTS ] <replaceable class="parameter">name</replaceable> (
<listitem>
<para>
The mode of an argument: either <literal>IN</>, <literal>OUT</>,
or <literal>INOUT</>. If omitted, the default is <literal>IN</>.
The mode of an argument: <literal>IN</>, <literal>OUT</>,
<literal>INOUT</>, or <literal>VARIADIC</>.
If omitted, the default is <literal>IN</>.
Note that <command>DROP FUNCTION</command> does not actually pay
any attention to <literal>OUT</> arguments, since only the input
arguments are needed to determine the function's identity.
So it is sufficient to list the <literal>IN</> and <literal>INOUT</>
arguments.
So it is sufficient to list the <literal>IN</>, <literal>INOUT</>,
and <literal>VARIADIC</> arguments.
</para>
</listitem>
</varlistentry>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/typeconv.sgml,v 1.54 2008/07/11 07:02:43 petere Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/typeconv.sgml,v 1.55 2008/07/16 01:30:21 tgl Exp $ -->
<chapter Id="typeconv">
<title>Type Conversion</title>
@ -503,6 +503,18 @@ different argument types are considered on an equal footing regardless of
search path position.
</para>
</step>
<step performance="optional">
<para>
If a function is declared with a <literal>VARIADIC</> array parameter, and
the call does not use the <literal>VARIADIC</> keyword, then the function
is treated as if the array parameter were replaced by one or more occurrences
of its element type, as needed to match the call. After such expansion the
function might have effective argument types identical to some non-variadic
function. In that case the function appearing earlier in the search path is
used, or if the two functions are in the same schema, the non-variadic one is
selected.
</para>
</step>
</substeps>
</step>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.130 2007/11/10 20:14:36 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.131 2008/07/16 01:30:21 tgl Exp $ -->
<sect1 id="xfunc">
<title>User-Defined Functions</title>
@ -495,7 +495,7 @@ SELECT getname(new_emp());
None
(1 row)
</screen>
</para>
</para>
<para>
Still another way to use a function that returns a composite type is to
@ -505,7 +505,7 @@ SELECT getname(new_emp());
</sect2>
<sect2 id="xfunc-output-parameters">
<title>Functions with Output Parameters</title>
<title><acronym>SQL</> Functions with Output Parameters</title>
<indexterm>
<primary>function</primary>
@ -578,9 +578,75 @@ DROP FUNCTION sum_n_product (int, int);
<para>
Parameters can be marked as <literal>IN</> (the default),
<literal>OUT</>, or <literal>INOUT</>. An <literal>INOUT</>
<literal>OUT</>, <literal>INOUT</>, or <literal>VARIADIC</>.
An <literal>INOUT</>
parameter serves as both an input parameter (part of the calling
argument list) and an output parameter (part of the result record type).
<literal>VARIADIC</> parameters are input parameters, but are treated
specially as described next.
</para>
</sect2>
<sect2 id="xfunc-sql-variadic-functions">
<title><acronym>SQL</> Functions with Variable Numbers of Arguments</title>
<indexterm>
<primary>function</primary>
<secondary>variadic</secondary>
</indexterm>
<indexterm>
<primary>variadic function</primary>
</indexterm>
<para>
<acronym>SQL</acronym> functions can be declared to accept
variable numbers of arguments, so long as all the <quote>optional</>
arguments are of the same data type. The optional arguments will be
passed to the function as an array. The function is declared by
marking the last parameter as <literal>VARIADIC</>; this parameter
must be declared as being of an array type. For example:
<screen>
CREATE FUNCTION mleast(VARIADIC numeric[]) RETURNS numeric AS $$
SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;
SELECT mleast(10, -1, 5, 4.4);
mleast
--------
-1
(1 row)
</screen>
Effectively, all the actual arguments at or beyond the
<literal>VARIADIC</> position are gathered up into a one-dimensional
array, as if you had written
<screen>
SELECT mleast(ARRAY[10, -1, 5, 4.4]); -- doesn't work
</screen>
You can't actually write that, though &mdash; or at least, it will
not match this function definition. A parameter marked
<literal>VARIADIC</> matches one or more occurrences of its element
type, not of its own type.
</para>
<para>
Sometimes it is useful to be able to pass an already-constructed array
to a variadic function; this is particularly handy when one variadic
function wants to pass on its array parameter to another one. You can
do that by specifying <literal>VARIADIC</> in the call:
<screen>
SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
</screen>
This prevents expansion of the function's variadic parameter into its
element type, thereby allowing the array argument value to match
normally. <literal>VARIADIC</> can only be attached to the last
actual argument of a function call.
</para>
</sect2>
@ -795,13 +861,45 @@ DETAIL: A function returning a polymorphic type must have at least one polymorp
For example:
<screen>
CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
AS 'select $1, array[$1,$1]' LANGUAGE SQL;
SELECT * FROM dup(22);
f2 | f3
----+---------
22 | {22,22}
(1 row)
</screen>
</para>
<para>
Polymorphism can also be used with variadic functions.
For example:
<screen>
CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;
SELECT anyleast(10, -1, 5, 4);
anyleast
----------
-1
(1 row)
SELECT anyleast('abc'::text, 'def');
anyleast
----------
abc
(1 row)
CREATE FUNCTION concat(text, VARIADIC anyarray) RETURNS text AS $$
SELECT array_to_string($2, $1);
$$ LANGUAGE SQL;
SELECT concat('|', 1, 4, 2);
concat
--------
1|4|2
(1 row)
</screen>
</para>
</sect2>
@ -852,6 +950,16 @@ CREATE FUNCTION test(smallint, double precision) RETURNS ...
avoid the problem by not choosing conflicting names.
</para>
<para>
Another possible conflict is between variadic and non-variadic functions.
For instance, it is possible to create both <literal>foo(numeric)</> and
<literal>foo(VARIADIC numeric[])</>. In this case it is unclear which one
should be matched to a call providing a single numeric argument, such as
<literal>foo(10.1)</>. The rule is that the function appearing
earlier in the search path is used, or if the two functions are in the
same schema, the non-variadic one is preferred.
</para>
<para>
When overloading C-language functions, there is an additional
constraint: The C name of each function in the family of
@ -2952,7 +3060,25 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
LANGUAGE C IMMUTABLE;
</programlisting>
</para>
<para>
There is a variant of polymorphism that is only available to C-language
functions: they can be declared to take parameters of type
<literal>"any"</>. (Note that this type name must be double-quoted,
since it's also a SQL reserved word.) This works like
<type>anyelement</> except that it does not constrain different
<literal>"any"</> arguments to be the same type, nor do they help
determine the function's result type. A C-language function can also
declare its final parameter to be <literal>VARIADIC "any"</>. This will
match one or more actual arguments of any type (not necessarily the same
type). These arguments will <emphasis>not</> be gathered into an array
as happens with normal variadic functions; they will just be passed to
the function separately. The <function>PG_NARGS()</> macro and the
methods described above must be used to determine the number of actual
arguments and their types when using this feature.
</para>
</sect2>
<sect2>
<title>Shared Memory and LWLocks</title>

View File

@ -4,7 +4,7 @@
*
* Copyright (c) 2003-2008, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.43 2008/01/01 19:45:48 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.44 2008/07/16 01:30:21 tgl Exp $
*/
/*
@ -1006,6 +1006,7 @@ CREATE VIEW parameters AS
WHEN proargmodes[(ss.x).n] = 'i' THEN 'IN'
WHEN proargmodes[(ss.x).n] = 'o' THEN 'OUT'
WHEN proargmodes[(ss.x).n] = 'b' THEN 'INOUT'
WHEN proargmodes[(ss.x).n] = 'v' THEN 'IN'
END AS character_data) AS parameter_mode,
CAST('NO' AS character_data) AS is_result,
CAST('NO' AS character_data) AS as_locator,

View File

@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.107 2008/07/01 02:09:34 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.108 2008/07/16 01:30:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -38,6 +38,7 @@
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
#include "storage/backendid.h"
#include "storage/ipc.h"
#include "utils/acl.h"
@ -561,24 +562,36 @@ TypeIsVisible(Oid typid)
* retrieve a list of the possible matches.
*
* If nargs is -1, we return all functions matching the given name,
* regardless of argument count.
* regardless of argument count. (expand_variadic must be false in this case.)
*
* If expand_variadic is true, then variadic functions having the same number
* or fewer arguments will be retrieved, with the variadic argument and any
* additional argument positions filled with the variadic element type.
* nvargs in the returned struct is set to the number of such arguments.
* If expand_variadic is false, variadic arguments are not treated specially,
* and the returned nvargs will always be zero.
*
* We search a single namespace if the function name is qualified, else
* all namespaces in the search path. The return list will never contain
* multiple entries with identical argument lists --- in the multiple-
* namespace case, we arrange for entries in earlier namespaces to mask
* identical entries in later namespaces.
* identical entries in later namespaces. We also arrange for non-variadic
* functions to mask variadic ones if the expanded argument list is the same.
*/
FuncCandidateList
FuncnameGetCandidates(List *names, int nargs)
FuncnameGetCandidates(List *names, int nargs, bool expand_variadic)
{
FuncCandidateList resultList = NULL;
bool any_variadic = false;
char *schemaname;
char *funcname;
Oid namespaceId;
CatCList *catlist;
int i;
/* check for caller error */
Assert(nargs >= 0 || !expand_variadic);
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &funcname);
@ -604,11 +617,57 @@ FuncnameGetCandidates(List *names, int nargs)
HeapTuple proctup = &catlist->members[i]->tuple;
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
int pronargs = procform->pronargs;
int effective_nargs;
int pathpos = 0;
bool variadic = false;
Oid va_elem_type = InvalidOid;
FuncCandidateList newResult;
/*
* Check if function is variadic, and get variadic element type if so.
* If expand_variadic is false, we can just ignore variadic-ness.
*
* XXX it's annoying to inject something as expensive as this even
* when there are no variadic functions involved. Find a better way.
*/
if (expand_variadic)
{
Datum proargmodes;
bool isnull;
proargmodes = SysCacheGetAttr(PROCOID, proctup,
Anum_pg_proc_proargmodes, &isnull);
if (!isnull)
{
ArrayType *ar = DatumGetArrayTypeP(proargmodes);
char *argmodes;
int j;
argmodes = ARR_DATA_PTR(ar);
j = ARR_DIMS(ar)[0] - 1;
if (j >= 0 && argmodes[j] == PROARGMODE_VARIADIC)
{
variadic = any_variadic = true;
switch (procform->proargtypes.values[j])
{
case ANYOID:
va_elem_type = ANYOID;
break;
case ANYARRAYOID:
va_elem_type = ANYELEMENTOID;
break;
default:
va_elem_type = get_element_type(procform->proargtypes.values[j]);
Assert(OidIsValid(va_elem_type));
break;
}
}
}
}
/* Ignore if it doesn't match requested argument count */
if (nargs >= 0 && pronargs != nargs)
if (nargs >= 0 &&
(variadic ? (pronargs > nargs) : (pronargs != nargs)))
continue;
if (OidIsValid(namespaceId))
@ -616,7 +675,6 @@ FuncnameGetCandidates(List *names, int nargs)
/* Consider only procs in specified namespace */
if (procform->pronamespace != namespaceId)
continue;
/* No need to check args, they must all be different */
}
else
{
@ -635,28 +693,63 @@ FuncnameGetCandidates(List *names, int nargs)
}
if (nsp == NULL)
continue; /* proc is not in search path */
}
/*
* We must compute the effective argument list so that we can easily
* compare it to earlier results. We waste a palloc cycle if it gets
* masked by an earlier result, but really that's a pretty infrequent
* case so it's not worth worrying about.
*/
effective_nargs = Max(pronargs, nargs);
newResult = (FuncCandidateList)
palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
+ effective_nargs * sizeof(Oid));
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = effective_nargs;
memcpy(newResult->args, procform->proargtypes.values,
pronargs * sizeof(Oid));
if (variadic)
{
int i;
newResult->nvargs = effective_nargs - pronargs + 1;
/* Expand variadic argument into N copies of element type */
for (i = pronargs - 1; i < effective_nargs; i++)
newResult->args[i] = va_elem_type;
}
else
newResult->nvargs = 0;
/*
* Does it have the same arguments as something we already accepted?
* If so, decide which one to keep. We can skip this check for the
* single-namespace case if no variadic match has been made, since
* then the unique index on pg_proc guarantees all the matches have
* different argument lists.
*/
if (any_variadic || !OidIsValid(namespaceId))
{
/*
* Okay, it's in the search path, but does it have the same
* arguments as something we already accepted? If so, keep only
* the one that appears earlier in the search path.
*
* If we have an ordered list from SearchSysCacheList (the normal
* case), then any conflicting proc must immediately adjoin this
* one in the list, so we only need to look at the newest result
* item. If we have an unordered list, we have to scan the whole
* result list.
* result list. Also, if either the current candidate or any
* previous candidate is a variadic match, we can't assume that
* conflicts are adjacent.
*/
if (resultList)
{
FuncCandidateList prevResult;
if (catlist->ordered)
if (catlist->ordered && !any_variadic)
{
if (pronargs == resultList->nargs &&
memcmp(procform->proargtypes.values,
if (effective_nargs == resultList->nargs &&
memcmp(newResult->args,
resultList->args,
pronargs * sizeof(Oid)) == 0)
effective_nargs * sizeof(Oid)) == 0)
prevResult = resultList;
else
prevResult = NULL;
@ -667,22 +760,58 @@ FuncnameGetCandidates(List *names, int nargs)
prevResult;
prevResult = prevResult->next)
{
if (pronargs == prevResult->nargs &&
memcmp(procform->proargtypes.values,
if (effective_nargs == prevResult->nargs &&
memcmp(newResult->args,
prevResult->args,
pronargs * sizeof(Oid)) == 0)
effective_nargs * sizeof(Oid)) == 0)
break;
}
}
if (prevResult)
{
/* We have a match with a previous result */
Assert(pathpos != prevResult->pathpos);
/*
* We have a match with a previous result. Prefer the
* one that's earlier in the search path.
*/
if (pathpos > prevResult->pathpos)
{
pfree(newResult);
continue; /* keep previous result */
}
else if (pathpos == prevResult->pathpos)
{
/*
* With variadic functions we could have, for example,
* both foo(numeric) and foo(variadic numeric[]) in
* the same namespace; if so we prefer the
* non-variadic match on efficiency grounds. It's
* also possible to have conflicting variadic
* functions, such as foo(numeric, variadic numeric[])
* and foo(variadic numeric[]). If you're silly
* enough to do that, we throw an error. (XXX It'd be
* better to detect such conflicts when the functions
* are created.)
*/
if (variadic)
{
if (prevResult->nvargs > 0)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("variadic function %s conflicts with another",
func_signature_string(names, pronargs,
procform->proargtypes.values))));
/* else, previous result wasn't variadic */
pfree(newResult);
continue; /* keep previous result */
}
/* non-variadic can replace a previous variadic */
Assert(prevResult->nvargs > 0);
}
/* replace previous result */
prevResult->pathpos = pathpos;
prevResult->oid = HeapTupleGetOid(proctup);
prevResult->oid = newResult->oid;
prevResult->nvargs = newResult->nvargs;
pfree(newResult);
continue; /* args are same, of course */
}
}
@ -691,15 +820,6 @@ FuncnameGetCandidates(List *names, int nargs)
/*
* Okay to add it to result list
*/
newResult = (FuncCandidateList)
palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
+ pronargs * sizeof(Oid));
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = pronargs;
memcpy(newResult->args, procform->proargtypes.values,
pronargs * sizeof(Oid));
newResult->next = resultList;
resultList = newResult;
}
@ -755,7 +875,8 @@ FunctionIsVisible(Oid funcid)
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs);
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
nargs, false);
for (; clist; clist = clist->next)
{
@ -1023,6 +1144,7 @@ OpernameGetCandidates(List *names, char oprkind)
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(opertup);
newResult->nargs = 2;
newResult->nvargs = 0;
newResult->args[0] = operform->oprleft;
newResult->args[1] = operform->oprright;
newResult->next = resultList;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.93 2008/06/19 00:46:04 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.94 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -293,6 +293,7 @@ lookup_agg_function(List *fnName,
{
Oid fnOid;
bool retset;
int nvargs;
Oid *true_oid_array;
FuncDetailCode fdresult;
AclResult aclresult;
@ -305,8 +306,8 @@ lookup_agg_function(List *fnName,
* function's return value. it also returns the true argument types to
* the function.
*/
fdresult = func_get_detail(fnName, NIL, nargs, input_types,
&fnOid, rettype, &retset,
fdresult = func_get_detail(fnName, NIL, nargs, input_types, false,
&fnOid, rettype, &retset, &nvargs,
&true_oid_array);
/* only valid case is a normal function not returning a set */

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.95 2008/07/12 10:44:56 petere Exp $
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.96 2008/07/16 01:30:22 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@ -173,6 +173,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
Datum *paramModes;
Datum *paramNames;
int outCount = 0;
int varCount = 0;
bool have_names = false;
ListCell *x;
int i;
@ -228,15 +229,41 @@ examine_parameter_list(List *parameters, Oid languageOid,
errmsg("functions cannot accept set arguments")));
if (fp->mode != FUNC_PARAM_OUT)
{
/* only OUT parameters can follow a VARIADIC parameter */
if (varCount > 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("VARIADIC parameter must be the last input parameter")));
inTypes[inCount++] = toid;
}
if (fp->mode != FUNC_PARAM_IN)
if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
{
if (outCount == 0) /* save first OUT param's type */
*requiredResultType = toid;
outCount++;
}
if (fp->mode == FUNC_PARAM_VARIADIC)
{
varCount++;
/* validate variadic parameter type */
switch (toid)
{
case ANYARRAYOID:
case ANYOID:
/* okay */
break;
default:
if (!OidIsValid(get_element_type(toid)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("VARIADIC parameter must be an array")));
break;
}
}
allTypes[i] = ObjectIdGetDatum(toid);
paramModes[i] = CharGetDatum(fp->mode);
@ -253,7 +280,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
/* Now construct the proper outputs as needed */
*parameterTypes = buildoidvector(inTypes, inCount);
if (outCount > 0)
if (outCount > 0 || varCount > 0)
{
*allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
sizeof(Oid), true, 'i');

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.394 2008/05/16 23:36:05 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.395 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1651,6 +1651,7 @@ _copyFuncCall(FuncCall *from)
COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(agg_star);
COPY_SCALAR_FIELD(agg_distinct);
COPY_SCALAR_FIELD(func_variadic);
COPY_SCALAR_FIELD(location);
return newnode;

View File

@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.323 2008/05/16 23:36:05 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.324 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1705,6 +1705,7 @@ _equalFuncCall(FuncCall *a, FuncCall *b)
COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(agg_star);
COMPARE_SCALAR_FIELD(agg_distinct);
COMPARE_SCALAR_FIELD(func_variadic);
COMPARE_SCALAR_FIELD(location);
return true;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.326 2008/04/29 14:59:16 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.327 2008/07/16 01:30:22 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -1610,6 +1610,7 @@ _outFuncCall(StringInfo str, FuncCall *node)
WRITE_NODE_FIELD(args);
WRITE_BOOL_FIELD(agg_star);
WRITE_BOOL_FIELD(agg_distinct);
WRITE_BOOL_FIELD(func_variadic);
WRITE_INT_FIELD(location);
}

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.616 2008/06/15 01:25:54 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.617 2008/07/16 01:30:22 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -444,7 +444,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
UPDATE USER USING
VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING
VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
@ -4200,10 +4200,11 @@ func_arg:
;
/* INOUT is SQL99 standard, IN OUT is for Oracle compatibility */
arg_class: IN_P { $$ = FUNC_PARAM_IN; }
| OUT_P { $$ = FUNC_PARAM_OUT; }
| INOUT { $$ = FUNC_PARAM_INOUT; }
| IN_P OUT_P { $$ = FUNC_PARAM_INOUT; }
arg_class: IN_P { $$ = FUNC_PARAM_IN; }
| OUT_P { $$ = FUNC_PARAM_OUT; }
| INOUT { $$ = FUNC_PARAM_INOUT; }
| IN_P OUT_P { $$ = FUNC_PARAM_INOUT; }
| VARIADIC { $$ = FUNC_PARAM_VARIADIC; }
;
/*
@ -7336,6 +7337,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($5, $1);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @2;
$$ = (Node *) n;
}
@ -7394,6 +7396,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($3, $5);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @4;
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n, @2);
}
@ -7406,6 +7409,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($4, $6);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @5;
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n, @2);
}
@ -7418,6 +7422,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($3, $5);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @4;
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n, @2);
}
@ -7430,6 +7435,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($4, $6);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @5;
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n, @2);
}
@ -7441,6 +7447,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($4, makeNullAConst());
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @2;
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2);
}
@ -7451,6 +7458,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($4, $6);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @5;
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2);
}
@ -7461,6 +7469,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($5, makeNullAConst());
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @5;
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2);
}
@ -7471,6 +7480,7 @@ a_expr: c_expr { $$ = $1; }
n->args = list_make2($5, $7);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @6;
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2);
}
@ -7862,6 +7872,7 @@ func_expr: func_name '(' ')'
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -7872,6 +7883,29 @@ func_expr: func_name '(' ')'
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
| func_name '(' VARIADIC a_expr ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = list_make1($4);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = TRUE;
n->location = @1;
$$ = (Node *)n;
}
| func_name '(' expr_list ',' VARIADIC a_expr ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = lappend($3, $6);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = TRUE;
n->location = @1;
$$ = (Node *)n;
}
@ -7886,6 +7920,7 @@ func_expr: func_name '(' ')'
* "must be an aggregate", but there's no provision
* for that in FuncCall at the moment.
*/
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -7896,6 +7931,7 @@ func_expr: func_name '(' ')'
n->args = $4;
n->agg_star = FALSE;
n->agg_distinct = TRUE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -7916,6 +7952,7 @@ func_expr: func_name '(' ')'
n->args = NIL;
n->agg_star = TRUE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -7974,6 +8011,7 @@ func_expr: func_name '(' ')'
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8043,6 +8081,7 @@ func_expr: func_name '(' ')'
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8053,6 +8092,7 @@ func_expr: func_name '(' ')'
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8063,6 +8103,7 @@ func_expr: func_name '(' ')'
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8073,6 +8114,7 @@ func_expr: func_name '(' ')'
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8085,6 +8127,7 @@ func_expr: func_name '(' ')'
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8100,6 +8143,7 @@ func_expr: func_name '(' ')'
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8111,6 +8155,7 @@ func_expr: func_name '(' ')'
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8124,6 +8169,7 @@ func_expr: func_name '(' ')'
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8143,6 +8189,7 @@ func_expr: func_name '(' ')'
n->args = list_make1($3);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8156,6 +8203,7 @@ func_expr: func_name '(' ')'
n->args = $4;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8166,6 +8214,7 @@ func_expr: func_name '(' ')'
n->args = $4;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8176,6 +8225,7 @@ func_expr: func_name '(' ')'
n->args = $4;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -8186,6 +8236,7 @@ func_expr: func_name '(' ')'
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = @1;
$$ = (Node *)n;
}
@ -9362,6 +9413,7 @@ reserved_keyword:
| UNIQUE
| USER
| USING
| VARIADIC
| WHEN
| WHERE
;
@ -9566,6 +9618,7 @@ makeOverlaps(List *largs, List *rargs, int location)
n->args = list_concat(largs, rargs);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
n->location = location;
return n;
}
@ -9625,7 +9678,7 @@ extractArgTypes(List *parameters)
{
FunctionParameter *p = (FunctionParameter *) lfirst(i);
if (p->mode != FUNC_PARAM_OUT) /* keep if IN or INOUT */
if (p->mode != FUNC_PARAM_OUT) /* keep if IN, INOUT, VARIADIC */
result = lappend(result, p->argType);
}
return result;

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.198 2008/07/03 20:58:46 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.199 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -393,6 +393,7 @@ const ScanKeyword ScanKeywords[] = {
{"value", VALUE_P, UNRESERVED_KEYWORD},
{"values", VALUES, COL_NAME_KEYWORD},
{"varchar", VARCHAR, COL_NAME_KEYWORD},
{"variadic", VARIADIC, RESERVED_KEYWORD},
{"varying", VARYING, UNRESERVED_KEYWORD},
{"verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD},
{"version", VERSION_P, UNRESERVED_KEYWORD},

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.228 2008/04/29 14:59:16 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.229 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -358,8 +358,8 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
result = ParseFuncOrColumn(pstate,
list_make1(n),
list_make1(result),
false, false, true,
-1);
false, false, false,
true, -1);
}
}
/* process trailing subscripts, if any */
@ -481,8 +481,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name2)),
list_make1(node),
false, false, true,
cref->location);
false, false, false,
true, cref->location);
}
break;
}
@ -511,8 +511,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name3)),
list_make1(node),
false, false, true,
cref->location);
false, false, false,
true, cref->location);
}
break;
}
@ -552,8 +552,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name4)),
list_make1(node),
false, false, true,
cref->location);
false, false, false,
true, cref->location);
}
break;
}
@ -1018,25 +1018,21 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
List *targs;
ListCell *args;
/*
* Transform the list of arguments. We use a shallow list copy and then
* transform-in-place to avoid O(N^2) behavior from repeated lappend's.
*
* XXX: repeated lappend() would no longer result in O(n^2) behavior;
* worth reconsidering this design?
*/
targs = list_copy(fn->args);
foreach(args, targs)
/* Transform the list of arguments ... */
targs = NIL;
foreach(args, fn->args)
{
lfirst(args) = transformExpr(pstate,
(Node *) lfirst(args));
targs = lappend(targs, transformExpr(pstate,
(Node *) lfirst(args)));
}
/* ... and hand off to ParseFuncOrColumn */
return ParseFuncOrColumn(pstate,
fn->funcname,
targs,
fn->agg_star,
fn->agg_distinct,
fn->func_variadic,
false,
fn->location);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.202 2008/03/26 21:10:38 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.203 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,14 +56,14 @@ static void unknown_attribute(ParseState *pstate, Node *relref, char *attname,
* intended to be used only to deliver an appropriate error message,
* not to affect the semantics. When is_column is true, we should have
* a single argument (the putative table), unqualified function name
* equal to the column name, and no aggregate decoration.
* equal to the column name, and no aggregate or variadic decoration.
*
* The argument expressions (in fargs) must have been transformed already.
*/
Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool is_column,
int location)
bool agg_star, bool agg_distinct, bool func_variadic,
bool is_column, int location)
{
Oid rettype;
Oid funcid;
@ -75,6 +75,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Oid *declared_arg_types;
Node *retval;
bool retset;
int nvargs;
FuncDetailCode fdresult;
/*
@ -126,9 +127,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* Check for column projection: if function has one argument, and that
* argument is of complex type, and function name is not qualified, then
* the "function call" could be a projection. We also check that there
* wasn't any aggregate decoration.
* wasn't any aggregate or variadic decoration.
*/
if (nargs == 1 && !agg_star && !agg_distinct && list_length(funcname) == 1)
if (nargs == 1 && !agg_star && !agg_distinct && !func_variadic &&
list_length(funcname) == 1)
{
Oid argtype = actual_arg_types[0];
@ -153,11 +155,15 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* func_get_detail looks up the function in the catalogs, does
* disambiguation for polymorphic functions, handles inheritance, and
* returns the funcid and type and set or singleton status of the
* function's return value. it also returns the true argument types to
* the function.
* function's return value. It also returns the true argument types to
* the function. (In the case of a variadic function call, the reported
* "true" types aren't really what is in pg_proc: the variadic argument is
* replaced by a suitable number of copies of its element type. We'll fix
* it up below.)
*/
fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
&funcid, &rettype, &retset,
!func_variadic,
&funcid, &rettype, &retset, &nvargs,
&declared_arg_types);
if (fdresult == FUNCDETAIL_COERCION)
{
@ -242,6 +248,34 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
/*
* If it's a variadic function call, transform the last nvargs arguments
* into an array --- unless it's an "any" variadic.
*/
if (nvargs > 0 && declared_arg_types[nargs - 1] != ANYOID)
{
ArrayExpr *newa = makeNode(ArrayExpr);
int non_var_args = nargs - nvargs;
List *vargs;
Assert(non_var_args >= 0);
vargs = list_copy_tail(fargs, non_var_args);
fargs = list_truncate(fargs, non_var_args);
newa->elements = vargs;
/* assume all the variadic arguments were coerced to the same type */
newa->element_typeid = exprType((Node *) linitial(vargs));
newa->array_typeid = get_array_type(newa->element_typeid);
if (!OidIsValid(newa->array_typeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(newa->element_typeid))));
newa->multidims = false;
fargs = lappend(fargs, newa);
}
/* build the appropriate output structure */
if (fdresult == FUNCDETAIL_NORMAL)
{
@ -668,21 +702,12 @@ func_select_candidate(int nargs,
* Find the named function in the system catalogs.
*
* Attempt to find the named function in the system catalogs with
* arguments exactly as specified, so that the normal case
* (exact match) is as quick as possible.
* arguments exactly as specified, so that the normal case (exact match)
* is as quick as possible.
*
* If an exact match isn't found:
* 1) check for possible interpretation as a type coercion request
* 2) get a vector of all possible input arg type arrays constructed
* from the superclasses of the original input arg types
* 3) get a list of all possible argument type arrays to the function
* with given name and number of arguments
* 4) for each input arg type array from vector #1:
* a) find how many of the function arg type arrays from list #2
* it can be coerced to
* b) if the answer is one, we have our function
* c) if the answer is more than one, attempt to resolve the conflict
* d) if the answer is zero, try the next array from vector #1
* 2) apply the ambiguous-function resolution rules
*
* Note: we rely primarily on nargs/argtypes as the argument description.
* The actual expression node list is passed in fargs so that we can check
@ -694,16 +719,18 @@ func_get_detail(List *funcname,
List *fargs,
int nargs,
Oid *argtypes,
bool expand_variadic,
Oid *funcid, /* return value */
Oid *rettype, /* return value */
bool *retset, /* return value */
int *nvargs, /* return value */
Oid **true_typeids) /* return value */
{
FuncCandidateList raw_candidates;
FuncCandidateList best_candidate;
/* Get list of possible candidates from namespace search */
raw_candidates = FuncnameGetCandidates(funcname, nargs);
raw_candidates = FuncnameGetCandidates(funcname, nargs, expand_variadic);
/*
* Quickly check if there is an exact match to the input datatypes (there
@ -786,6 +813,7 @@ func_get_detail(List *funcname,
*funcid = InvalidOid;
*rettype = targetType;
*retset = false;
*nvargs = 0;
*true_typeids = argtypes;
return FUNCDETAIL_COERCION;
}
@ -835,6 +863,7 @@ func_get_detail(List *funcname,
FuncDetailCode result;
*funcid = best_candidate->oid;
*nvargs = best_candidate->nvargs;
*true_typeids = best_candidate->args;
ftup = SearchSysCache(PROCOID,
@ -1189,7 +1218,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
FuncCandidateList clist;
clist = FuncnameGetCandidates(funcname, nargs);
clist = FuncnameGetCandidates(funcname, nargs, false);
while (clist)
{

View File

@ -19,7 +19,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.13 2008/04/29 14:59:17 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.14 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -388,6 +388,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
funccallnode->args = list_make1(castnode);
funccallnode->agg_star = false;
funccallnode->agg_distinct = false;
funccallnode->func_variadic = false;
funccallnode->location = -1;
constraint = makeNode(Constraint);

View File

@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.107 2008/06/19 00:46:05 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.108 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -131,7 +131,7 @@ regprocin(PG_FUNCTION_ARGS)
* pg_proc entries in the current search path.
*/
names = stringToQualifiedNameList(pro_name_or_oid);
clist = FuncnameGetCandidates(names, -1);
clist = FuncnameGetCandidates(names, -1, false);
if (clist == NULL)
ereport(ERROR,
@ -189,7 +189,8 @@ regprocout(PG_FUNCTION_ARGS)
* Would this proc be found (uniquely!) by regprocin? If not,
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1);
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-1, false);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
@ -276,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS)
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
clist = FuncnameGetCandidates(names, nargs);
clist = FuncnameGetCandidates(names, nargs, false);
for (; clist; clist = clist->next)
{

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.275 2008/06/06 17:59:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.276 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -193,7 +193,8 @@ static Node *processIndirection(Node *node, deparse_context *context,
bool printit);
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *generate_relation_name(Oid relid);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes,
bool *is_variadic);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
@ -531,7 +532,7 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
appendStringInfo(&buf, "FOR EACH STATEMENT ");
appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
generate_function_name(trigrec->tgfoid, 0, NULL));
generate_function_name(trigrec->tgfoid, 0, NULL, NULL));
if (trigrec->tgnargs > 0)
{
@ -4293,6 +4294,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
Oid funcoid = expr->funcid;
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
bool is_variadic;
ListCell *l;
/*
@ -4343,8 +4345,17 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
}
appendStringInfo(buf, "%s(",
generate_function_name(funcoid, nargs, argtypes));
get_rule_expr((Node *) expr->args, context, true);
generate_function_name(funcoid, nargs, argtypes,
&is_variadic));
nargs = 0;
foreach(l, expr->args)
{
if (nargs++ > 0)
appendStringInfoString(buf, ", ");
if (is_variadic && lnext(l) == NULL)
appendStringInfoString(buf, "VARIADIC ");
get_rule_expr((Node *) lfirst(l), context, true);
}
appendStringInfoChar(buf, ')');
}
@ -4371,7 +4382,8 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
}
appendStringInfo(buf, "%s(%s",
generate_function_name(aggref->aggfnoid, nargs, argtypes),
generate_function_name(aggref->aggfnoid,
nargs, argtypes, NULL),
aggref->aggdistinct ? "DISTINCT " : "");
/* aggstar can be set only in zero-argument aggregates */
if (aggref->aggstar)
@ -5329,10 +5341,12 @@ generate_relation_name(Oid relid)
* given that it is being called with the specified actual arg types.
* (Arg types matter because of ambiguous-function resolution rules.)
*
* The result includes all necessary quoting and schema-prefixing.
* The result includes all necessary quoting and schema-prefixing. We can
* also pass back an indication of whether the function is variadic.
*/
static char *
generate_function_name(Oid funcid, int nargs, Oid *argtypes)
generate_function_name(Oid funcid, int nargs, Oid *argtypes,
bool *is_variadic)
{
HeapTuple proctup;
Form_pg_proc procform;
@ -5343,6 +5357,7 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes)
Oid p_funcid;
Oid p_rettype;
bool p_retset;
int p_nvargs;
Oid *p_true_typeids;
proctup = SearchSysCache(PROCOID,
@ -5352,7 +5367,7 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes)
elog(ERROR, "cache lookup failed for function %u", funcid);
procform = (Form_pg_proc) GETSTRUCT(proctup);
proname = NameStr(procform->proname);
Assert(nargs == procform->pronargs);
Assert(nargs >= procform->pronargs);
/*
* The idea here is to schema-qualify only if the parser would fail to
@ -5360,9 +5375,9 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes)
* specified argtypes.
*/
p_result = func_get_detail(list_make1(makeString(proname)),
NIL, nargs, argtypes,
NIL, nargs, argtypes, false,
&p_funcid, &p_rettype,
&p_retset, &p_true_typeids);
&p_retset, &p_nvargs, &p_true_typeids);
if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) &&
p_funcid == funcid)
nspname = NULL;
@ -5371,6 +5386,34 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes)
result = quote_qualified_identifier(nspname, proname);
/* Check variadic-ness if caller cares */
if (is_variadic)
{
/* XXX change this if we simplify code in FuncnameGetCandidates */
Datum proargmodes;
bool isnull;
*is_variadic = false;
proargmodes = SysCacheGetAttr(PROCOID, proctup,
Anum_pg_proc_proargmodes, &isnull);
if (!isnull)
{
ArrayType *ar = DatumGetArrayTypeP(proargmodes);
char *argmodes;
int j;
argmodes = ARR_DATA_PTR(ar);
j = ARR_DIMS(ar)[0] - 1;
if (j >= 0 && argmodes[j] == PROARGMODE_VARIADIC)
{
/* "any" variadics are not treated as variadics for listing */
if (procform->proargtypes.values[j] != ANYOID)
*is_variadic = true;
}
}
}
ReleaseSysCache(proctup);
return result;

View File

@ -7,7 +7,7 @@
* Copyright (c) 2002-2008, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.39 2008/03/25 22:42:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.40 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -844,7 +844,8 @@ get_func_result_name(Oid functionId)
numoutargs = 0;
for (i = 0; i < numargs; i++)
{
if (argmodes[i] == PROARGMODE_IN)
if (argmodes[i] == PROARGMODE_IN ||
argmodes[i] == PROARGMODE_VARIADIC)
continue;
Assert(argmodes[i] == PROARGMODE_OUT ||
argmodes[i] == PROARGMODE_INOUT);
@ -994,7 +995,8 @@ build_function_result_tupdesc_d(Datum proallargtypes,
{
char *pname;
if (argmodes[i] == PROARGMODE_IN)
if (argmodes[i] == PROARGMODE_IN ||
argmodes[i] == PROARGMODE_VARIADIC)
continue;
Assert(argmodes[i] == PROARGMODE_OUT ||
argmodes[i] == PROARGMODE_INOUT);

View File

@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.493 2008/07/01 11:46:48 heikki Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.494 2008/07/16 01:30:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -6435,15 +6435,18 @@ format_function_arguments(FuncInfo *finfo, int nallargs,
{
switch (argmodes[j][0])
{
case 'i':
case PROARGMODE_IN:
argmode = "";
break;
case 'o':
case PROARGMODE_OUT:
argmode = "OUT ";
break;
case 'b':
case PROARGMODE_INOUT:
argmode = "INOUT ";
break;
case PROARGMODE_VARIADIC:
argmode = "VARIADIC ";
break;
default:
write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
argmode = "";

View File

@ -8,7 +8,7 @@
*
* Copyright (c) 2000-2008, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.181 2008/07/15 16:06:06 momjian Exp $
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.182 2008/07/16 01:30:23 tgl Exp $
*/
#include "postgres_fe.h"
@ -204,7 +204,8 @@ describeFunctions(const char *pattern, bool verbose)
" CASE\n"
" WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
" WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n"
" WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
" WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
" WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n"
" END ||\n"
" CASE\n"
" WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n"

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.468 2008/07/16 00:48:53 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.469 2008/07/16 01:30:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200807151
#define CATALOG_VERSION_NO 200807152
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.54 2008/07/01 02:09:34 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.55 2008/07/16 01:30:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -29,6 +29,7 @@ typedef struct _FuncCandidateList
int pathpos; /* for internal use of namespace lookup */
Oid oid; /* the function or operator's OID */
int nargs; /* number of arg types returned */
int nvargs; /* number of args to become variadic array */
Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */
} *FuncCandidateList; /* VARIABLE LENGTH STRUCT */
@ -51,7 +52,8 @@ extern bool RelationIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs);
extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
bool expand_variadic);
extern bool FunctionIsVisible(Oid funcid);
extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.506 2008/07/16 00:48:53 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.507 2008/07/16 01:30:23 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@ -4474,5 +4474,6 @@ DESCR("is txid visible in snapshot?");
#define PROARGMODE_IN 'i'
#define PROARGMODE_OUT 'o'
#define PROARGMODE_INOUT 'b'
#define PROARGMODE_VARIADIC 'v'
#endif /* PG_PROC_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.366 2008/05/16 23:36:05 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.367 2008/07/16 01:30:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -261,6 +261,7 @@ typedef struct FuncCall
List *args; /* the arguments (list of exprs) */
bool agg_star; /* argument was really '*' */
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
int location; /* token location, or -1 if unknown */
} FuncCall;
@ -1568,7 +1569,8 @@ typedef enum FunctionParameterMode
/* the assigned enum values appear in pg_proc, don't change 'em! */
FUNC_PARAM_IN = 'i', /* input only */
FUNC_PARAM_OUT = 'o', /* output only */
FUNC_PARAM_INOUT = 'b' /* both */
FUNC_PARAM_INOUT = 'b', /* both */
FUNC_PARAM_VARIADIC = 'v' /* variadic */
} FunctionParameterMode;
typedef struct FunctionParameter

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.59 2008/01/01 19:45:58 momjian Exp $
* $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.60 2008/07/16 01:30:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,13 +43,13 @@ typedef enum
extern Node *ParseFuncOrColumn(ParseState *pstate,
List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool is_column,
int location);
bool agg_star, bool agg_distinct, bool func_variadic,
bool is_column, int location);
extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
int nargs, Oid *argtypes,
int nargs, Oid *argtypes, bool expand_variadic,
Oid *funcid, Oid *rettype,
bool *retset, Oid **true_typeids);
bool *retset, int *nvargs, Oid **true_typeids);
extern int func_match_argtypes(int nargs,
Oid *input_typeids,

View File

@ -1,4 +1,4 @@
/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.368 2008/06/26 08:04:05 meskes Exp $ */
/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.369 2008/07/16 01:30:23 tgl Exp $ */
/* Copyright comment */
%{
@ -489,7 +489,7 @@ add_typedef(char *name, char * dimension, char * length, enum ECPGttype type_enu
UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
UPDATE USER USING
VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING
VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
@ -2629,6 +2629,7 @@ arg_class: IN_P { $$ = make_str("in"); }
| OUT_P { $$ = make_str("out"); }
| INOUT { $$ = make_str("inout"); }
| IN_P OUT_P { $$ = make_str("in out"); }
| VARIADIC { $$ = make_str("variadic"); }
;
func_as: StringConst
@ -6857,6 +6858,7 @@ reserved_keyword:
| UNIQUE { $$ = make_str("unique"); }
| USER { $$ = make_str("user"); }
| USING { $$ = make_str("using"); }
| VARIADIC { $$ = make_str("variadic"); }
| WHEN { $$ = make_str("when"); }
| WHERE { $$ = make_str("where"); }
;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.126 2008/05/13 22:10:29 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.127 2008/07/16 01:30:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -426,7 +426,8 @@ do_compile(FunctionCallInfo fcinfo,
{
argitemtype = PLPGSQL_NSTYPE_VAR;
/* input argument vars are forced to be CONSTANT */
if (argmode == PROARGMODE_IN)
if (argmode == PROARGMODE_IN ||
argmode == PROARGMODE_VARIADIC)
((PLpgSQL_var *) argvariable)->isconst = true;
}
else
@ -436,7 +437,9 @@ do_compile(FunctionCallInfo fcinfo,
}
/* Remember arguments in appropriate arrays */
if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT)
if (argmode == PROARGMODE_IN ||
argmode == PROARGMODE_INOUT ||
argmode == PROARGMODE_VARIADIC)
in_arg_varnos[num_in_args++] = argvariable->dno;
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT)
out_arg_variables[num_out_args++] = argvariable;

View File

@ -1,7 +1,7 @@
/**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL
*
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.110 2008/05/12 00:00:54 alvherre Exp $
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.111 2008/07/16 01:30:23 tgl Exp $
*
*********************************************************************
*/
@ -1271,7 +1271,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
/* proc->nargs was initialized to 0 above */
for (i = 0; i < total; i++)
{
if (modes[i] != 'o')
if (modes[i] != PROARGMODE_OUT)
(proc->nargs)++;
}
}
@ -1282,8 +1282,8 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
HeapTuple argTypeTup;
Form_pg_type argTypeStruct;
if (modes && modes[i] == 'o') /* skip OUT arguments */
continue;
if (modes && modes[i] == PROARGMODE_OUT)
continue; /* skip OUT arguments */
Assert(types[i] == procStruct->proargtypes.values[pos]);

View File

@ -3544,3 +3544,93 @@ select case_test(13);
drop function catch();
drop function case_test(bigint);
-- test variadic functions
create or replace function vari(variadic int[])
returns void as $$
begin
for i in array_lower($1,1)..array_upper($1,1) loop
raise notice '%', $1[i];
end loop; end;
$$ language plpgsql;
select vari(1,2,3,4,5);
NOTICE: 1
NOTICE: 2
NOTICE: 3
NOTICE: 4
NOTICE: 5
vari
------
(1 row)
select vari(3,4,5);
NOTICE: 3
NOTICE: 4
NOTICE: 5
vari
------
(1 row)
select vari(variadic array[5,6,7]);
NOTICE: 5
NOTICE: 6
NOTICE: 7
vari
------
(1 row)
drop function vari(int[]);
-- coercion test
create or replace function pleast(variadic numeric[])
returns numeric as $$
declare aux numeric = $1[array_lower($1,1)];
begin
for i in array_lower($1,1)+1..array_upper($1,1) loop
if $1[i] < aux then aux := $1[i]; end if;
end loop;
return aux;
end;
$$ language plpgsql immutable strict;
select pleast(10,1,2,3,-16);
pleast
--------
-16
(1 row)
select pleast(10.2,2.2,-1.1);
pleast
--------
-1.1
(1 row)
select pleast(10.2,10, -20);
pleast
--------
-20
(1 row)
select pleast(10,20, -1.0);
pleast
--------
-1.0
(1 row)
-- in case of conflict, non-variadic version is preferred
create or replace function pleast(numeric)
returns numeric as $$
begin
raise notice 'non-variadic function called';
return $1;
end;
$$ language plpgsql immutable strict;
select pleast(10);
NOTICE: non-variadic function called
pleast
--------
10
(1 row)
drop function pleast(numeric[]);
drop function pleast(numeric);

View File

@ -613,3 +613,111 @@ create aggregate build_group(int8, integer) (
SFUNC = add_group,
STYPE = int8[]
);
-- test variadic polymorphic functions
create function myleast(variadic anyarray) returns anyelement as $$
select min($1[i]) from generate_subscripts($1,1) g(i)
$$ language sql immutable strict;
select myleast(10, 1, 20, 33);
myleast
---------
1
(1 row)
select myleast(1.1, 0.22, 0.55);
myleast
---------
0.22
(1 row)
select myleast('z'::text);
myleast
---------
z
(1 row)
select myleast(); -- fail
ERROR: function myleast() does not exist
LINE 1: select myleast();
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
-- test with variadic call parameter
select myleast(variadic array[1,2,3,4,-1]);
myleast
---------
-1
(1 row)
select myleast(variadic array[1.1, -5.5]);
myleast
---------
-5.5
(1 row)
--test with empty variadic call parameter
select myleast(variadic array[]::int[]);
myleast
---------
(1 row)
-- an example with some ordinary arguments too
create function concat(text, variadic anyarray) returns text as $$
select array_to_string($2, $1);
$$ language sql immutable strict;
select concat('%', 1, 2, 3, 4, 5);
concat
-----------
1%2%3%4%5
(1 row)
select concat('|', 'a'::text, 'b', 'c');
concat
--------
a|b|c
(1 row)
select concat('|', variadic array[1,2,33]);
concat
--------
1|2|33
(1 row)
select concat('|', variadic array[]::int[]);
concat
--------
(1 row)
drop function concat(text, anyarray);
-- mix variadic with anyelement
create function formarray(anyelement, variadic anyarray) returns anyarray as $$
select array_prepend($1, $2);
$$ language sql immutable strict;
select formarray(1,2,3,4,5);
formarray
-------------
{1,2,3,4,5}
(1 row)
select formarray(1.1, variadic array[1.2,55.5]);
formarray
----------------
{1.1,1.2,55.5}
(1 row)
select formarray(1.1, array[1.2,55.5]); -- fail without variadic
ERROR: function formarray(numeric, numeric[]) does not exist
LINE 1: select formarray(1.1, array[1.2,55.5]);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select formarray(1, 'x'::text); -- fail, type mismatch
ERROR: function formarray(integer, text) does not exist
LINE 1: select formarray(1, 'x'::text);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select formarray(1, variadic array['x'::text]); -- fail, type mismatch
ERROR: function formarray(integer, text[]) does not exist
LINE 1: select formarray(1, variadic array['x'::text]);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function formarray(anyelement, variadic anyarray);

View File

@ -2878,3 +2878,50 @@ select case_test(13);
drop function catch();
drop function case_test(bigint);
-- test variadic functions
create or replace function vari(variadic int[])
returns void as $$
begin
for i in array_lower($1,1)..array_upper($1,1) loop
raise notice '%', $1[i];
end loop; end;
$$ language plpgsql;
select vari(1,2,3,4,5);
select vari(3,4,5);
select vari(variadic array[5,6,7]);
drop function vari(int[]);
-- coercion test
create or replace function pleast(variadic numeric[])
returns numeric as $$
declare aux numeric = $1[array_lower($1,1)];
begin
for i in array_lower($1,1)+1..array_upper($1,1) loop
if $1[i] < aux then aux := $1[i]; end if;
end loop;
return aux;
end;
$$ language plpgsql immutable strict;
select pleast(10,1,2,3,-16);
select pleast(10.2,2.2,-1.1);
select pleast(10.2,10, -20);
select pleast(10,20, -1.0);
-- in case of conflict, non-variadic version is preferred
create or replace function pleast(numeric)
returns numeric as $$
begin
raise notice 'non-variadic function called';
return $1;
end;
$$ language plpgsql immutable strict;
select pleast(10);
drop function pleast(numeric[]);
drop function pleast(numeric);

View File

@ -426,3 +426,46 @@ create aggregate build_group(int8, integer) (
SFUNC = add_group,
STYPE = int8[]
);
-- test variadic polymorphic functions
create function myleast(variadic anyarray) returns anyelement as $$
select min($1[i]) from generate_subscripts($1,1) g(i)
$$ language sql immutable strict;
select myleast(10, 1, 20, 33);
select myleast(1.1, 0.22, 0.55);
select myleast('z'::text);
select myleast(); -- fail
-- test with variadic call parameter
select myleast(variadic array[1,2,3,4,-1]);
select myleast(variadic array[1.1, -5.5]);
--test with empty variadic call parameter
select myleast(variadic array[]::int[]);
-- an example with some ordinary arguments too
create function concat(text, variadic anyarray) returns text as $$
select array_to_string($2, $1);
$$ language sql immutable strict;
select concat('%', 1, 2, 3, 4, 5);
select concat('|', 'a'::text, 'b', 'c');
select concat('|', variadic array[1,2,33]);
select concat('|', variadic array[]::int[]);
drop function concat(text, anyarray);
-- mix variadic with anyelement
create function formarray(anyelement, variadic anyarray) returns anyarray as $$
select array_prepend($1, $2);
$$ language sql immutable strict;
select formarray(1,2,3,4,5);
select formarray(1.1, variadic array[1.2,55.5]);
select formarray(1.1, array[1.2,55.5]); -- fail without variadic
select formarray(1, 'x'::text); -- fail, type mismatch
select formarray(1, variadic array['x'::text]); -- fail, type mismatch
drop function formarray(anyelement, variadic anyarray);