Support use of function argument names to identify which actual arguments

match which function parameters.  The syntax uses AS, for example
	funcname(value AS arg1, anothervalue AS arg2)

Pavel Stehule
This commit is contained in:
Tom Lane 2009-10-08 02:39:25 +00:00
parent 2eda8dfb52
commit 717fa274d1
34 changed files with 1925 additions and 274 deletions

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.87 2009/10/02 18:13:04 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.88 2009/10/08 02:39:14 tgl Exp $
-->
<refentry id="SQL-CREATEFUNCTION">
@ -65,7 +65,7 @@ CREATE [ OR REPLACE ] FUNCTION
Also, <command>CREATE OR REPLACE FUNCTION</command> will not let
you change the return type of an existing function. To do that,
you must drop and recreate the function. (When using <literal>OUT</>
parameters, that means you cannot change the names or types of any
parameters, that means you cannot change the types of any
<literal>OUT</> parameters except by dropping the function.)
</para>
@ -121,8 +121,11 @@ CREATE [ OR REPLACE ] FUNCTION
<para>
The name of an argument. Some languages (currently only PL/pgSQL) let
you use the name in the function body. For other languages the
name of an input argument is just extra documentation. But the name
of an output argument is significant, since it defines the column
name of an input argument is just extra documentation, so far as
the function itself is concerned; but you can use input argument names
when calling a function to improve readability (see <xref
linkend="sql-syntax-calling-funcs">). In any case, the name
of an output argument is significant, because it defines the column
name in the result row type. (If you omit the name for an output
argument, the system will choose a default column name.)
</para>
@ -570,6 +573,18 @@ CREATE FUNCTION foo(int, int default 42) ...
to replace it (this includes being a member of the owning role).
</para>
<para>
When replacing an existing function with <command>CREATE OR REPLACE
FUNCTION</>, there are restrictions on changing parameter names.
You cannot change the name already assigned to any input parameter
(although you can add names to parameters that had none before).
If there is more than one output parameter, you cannot change the
names of the output parameters, because that would change the
column names of the anonymous composite type that describes the
function's result. These restrictions are made to ensure that
existing calls of the function do not stop working when it is replaced.
</para>
</refsect1>
<refsect1 id="sql-createfunction-examples">

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.34 2009/06/04 18:33:06 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.35 2009/10/08 02:39:16 tgl Exp $ -->
<chapter id="source">
<title>PostgreSQL Coding Conventions</title>
@ -125,7 +125,7 @@ ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function %s is not unique",
func_signature_string(funcname, nargs,
actual_arg_types)),
NIL, actual_arg_types)),
errhint("Unable to choose a best candidate function. "
"You might need to add explicit typecasts.")));
</programlisting>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.136 2009/09/22 23:52:53 petere Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.137 2009/10/08 02:39:16 tgl Exp $ -->
<chapter id="sql-syntax">
<title>SQL Syntax</title>
@ -1505,6 +1505,11 @@ sqrt(2)
The list of built-in functions is in <xref linkend="functions">.
Other functions can be added by the user.
</para>
<para>
The arguments can optionally have names attached.
See <xref linkend="sql-syntax-calling-funcs"> for details.
</para>
</sect2>
<sect2 id="syntax-aggregates">
@ -2123,4 +2128,168 @@ SELECT ... WHERE CASE WHEN x &gt; 0 THEN y/x &gt; 1.5 ELSE false END;
</sect2>
</sect1>
<sect1 id="sql-syntax-calling-funcs">
<title>Calling Functions</title>
<indexterm zone="sql-syntax-calling-funcs">
<primary>notation</primary>
<secondary>functions</secondary>
</indexterm>
<para>
<productname>PostgreSQL</productname> allows functions that have named
parameters to be called using either <firstterm>positional</firstterm> or
<firstterm>named</firstterm> notation. Named notation is especially
useful for functions that have a large number of parameters, since it
makes the associations between parameters and actual arguments more
explicit and reliable.
In positional notation, a function call is written with
its argument values in the same order as they are defined in the function
declaration. In named notation, the arguments are matched to the
function parameters by name and can be written in any order.
</para>
<para>
In either notation, parameters that have default values given in the
function declaration need not be written in the call at all. But this
is particularly useful in named notation, since any combination of
parameters can be omitted; while in positional notation parameters can
only be omitted from right to left.
</para>
<para>
<productname>PostgreSQL</productname> also supports
<firstterm>mixed</firstterm> notation, which combines positional and
named notation. In this case, positional parameters are written first
and named parameters appear after them.
</para>
<para>
The following examples will illustrate the usage of all three
notations, using the following function definition:
<programlisting>
CREATE FUNCTION concat_lower_or_upper(a text, b text, uppercase boolean DEFAULT false)
RETURNS text
AS
$$
SELECT CASE
WHEN $3 THEN UPPER($1 || ' ' || $2)
ELSE LOWER($1 || ' ' || $2)
END;
$$
LANGUAGE SQL IMMUTABLE STRICT;
</programlisting>
Function <function>concat_lower_or_upper</function> has two mandatory
parameters, <literal>a</literal> and <literal>b</literal>. Additionally
there is one optional parameter <literal>uppercase</literal> which defaults
to <literal>false</literal>. The <literal>a</literal> and
<literal>b</literal> inputs will be concatenated, and forced to either
upper or lower case depending on the <literal>uppercase</literal>
parameter. The remaining details of this function
definition are not important here (see <xref linkend="extend"> for
more information).
</para>
<sect2 id="sql-syntax-calling-funcs-positional">
<title>Using positional notation</title>
<indexterm>
<primary>function</primary>
<secondary>positional notation</secondary>
</indexterm>
<para>
Positional notation is the traditional mechanism for passing arguments
to functions in <productname>PostgreSQL</productname>. An example is:
<screen>
SELECT concat_lower_or_upper('Hello', 'World', true);
concat_lower_or_upper
-----------------------
HELLO WORLD
(1 row)
</screen>
All arguments are specified in order. The result is upper case since
<literal>uppercase</literal> is specified as <literal>true</literal>.
Another example is:
<screen>
SELECT concat_lower_or_upper('Hello', 'World');
concat_lower_or_upper
-----------------------
hello world
(1 row)
</screen>
Here, the <literal>uppercase</literal> parameter is omitted, so it
receives its default value of <literal>false</literal>, resulting in
lower case output. In positional notation, arguments can be omitted
from right to left so long as they have defaults.
</para>
</sect2>
<sect2 id="sql-syntax-calling-funcs-named">
<title>Using named notation</title>
<indexterm>
<primary>function</primary>
<secondary>named notation</secondary>
</indexterm>
<para>
In named notation, each argument's name is specified using the
<literal>AS</literal> keyword. For example:
<screen>
SELECT concat_lower_or_upper('Hello' AS a, 'World' AS b);
concat_lower_or_upper
-----------------------
hello world
(1 row)
</screen>
Again, the argument <literal>uppercase</literal> was omitted
so it is set to <literal>false</literal> implicitly. One advantage of
using named notation is that the arguments may be specified in any
order, for example:
<screen>
SELECT concat_lower_or_upper('Hello' AS a, 'World' AS b, true AS uppercase);
concat_lower_or_upper
-----------------------
HELLO WORLD
(1 row)
SELECT concat_lower_or_upper('Hello' AS a, true AS uppercase, 'World' AS b);
concat_lower_or_upper
-----------------------
HELLO WORLD
(1 row)
</screen>
</para>
</sect2>
<sect2 id="sql-syntax-calling-funcs-mixed">
<title>Using mixed notation</title>
<indexterm>
<primary>function</primary>
<secondary>mixed notation</secondary>
</indexterm>
<para>
The mixed notation combines positional and named notation. However, as
already mentioned, named arguments cannot precede positional arguments.
For example:
<screen>
SELECT concat_lower_or_upper('Hello', 'World', true AS uppercase);
concat_lower_or_upper
-----------------------
HELLO WORLD
(1 row)
</screen>
In the above query, the arguments <literal>a</literal> and
<literal>b</literal> are specified positionally, while
<literal>uppercase</> is specified by name. In this example,
that adds little except documentation. With a more complex function
having numerous parameters that have default values, named or mixed
notation can save a great deal of writing and reduce chances for error.
</para>
</sect2>
</sect1>
</chapter>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.139 2009/09/03 22:11:07 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.140 2009/10/08 02:39:16 tgl Exp $ -->
<sect1 id="xfunc">
<title>User-Defined Functions</title>
@ -517,6 +517,39 @@ SELECT getname(new_emp());
</para>
</sect2>
<sect2 id="xfunc-named-parameters">
<title><acronym>SQL</> Functions with Parameter Names</title>
<indexterm>
<primary>function</primary>
<secondary>named parameter</secondary>
</indexterm>
<para>
It is possible to attach names to a function's parameters, for example
<programlisting>
CREATE FUNCTION tf1 (acct_no integer, debit numeric) RETURNS numeric AS $$
UPDATE bank
SET balance = balance - $2
WHERE accountno = $1
RETURNING balance;
$$ LANGUAGE SQL;
</programlisting>
Here the first parameter has been given the name <literal>acct_no</>,
and the second parameter the name <literal>debit</>.
So far as the SQL function itself is concerned, these names are just
decoration; you must still refer to the parameters as <literal>$1</>,
<literal>$2</>, etc within the function body. (Some procedural
languages let you use the parameter names instead.) However,
attaching names to the parameters is useful for documentation purposes.
When a function has many parameters, it is also useful to use the names
while calling the function, as described in
<xref linkend="sql-syntax-calling-funcs">.
</para>
</sect2>
<sect2 id="xfunc-output-parameters">
<title><acronym>SQL</> Functions with Output Parameters</title>
@ -571,7 +604,10 @@ LANGUAGE SQL;
</screen>
but not having to bother with the separate composite type definition
is often handy.
is often handy. Notice that the names attached to the output parameters
are not just decoration, but determine the column names of the anonymous
composite type. (If you omit a name for an output parameter, the
system will choose a name on its own.)
</para>
<para>
@ -621,7 +657,7 @@ DROP FUNCTION sum_n_product (int, int);
must be declared as being of an array type. For example:
<screen>
CREATE FUNCTION mleast(VARIADIC numeric[]) RETURNS numeric AS $$
CREATE FUNCTION mleast(VARIADIC arr numeric[]) RETURNS numeric AS $$
SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;
@ -661,6 +697,25 @@ SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
normally. <literal>VARIADIC</> can only be attached to the last
actual argument of a function call.
</para>
<para>
The array element parameters generated from a variadic parameter are
treated as not having any names of their own. This means it is not
possible to call a variadic function using named arguments (<xref
linkend="sql-syntax-calling-funcs">), except when you specify
<literal>VARIADIC</>. For example, this will work:
<screen>
SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4] AS arr);
</screen>
but not these:
<screen>
SELECT mleast(10 AS arr);
SELECT mleast(ARRAY[10, -1, 5, 4.4] AS arr);
</screen>
</para>
</sect2>
<sect2 id="xfunc-sql-parameter-defaults">
@ -677,7 +732,9 @@ SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
called with insufficiently many actual arguments. Since arguments
can only be omitted from the end of the actual argument list, all
parameters after a parameter with a default value have to have
default values as well.
default values as well. (Although the use of named argument notation
could allow this restriction to be relaxed, it's still enforced so that
positional argument notation works sensibly.)
</para>
<para>
@ -712,7 +769,7 @@ SELECT foo(); -- fails since there is no default for the first argument
ERROR: function foo() does not exist
</screen>
The <literal>=</literal> sign can also be used in place of the
key word <literal>DEFAULT</literal>,
key word <literal>DEFAULT</literal>.
</para>
</sect2>

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.118 2009/06/11 14:48:55 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.119 2009/10/08 02:39:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -36,6 +36,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
@ -188,6 +189,8 @@ static void InitTempTableNamespace(void);
static void RemoveTempRelations(Oid tempNamespaceId);
static void RemoveTempRelationsCallback(int code, Datum arg);
static void NamespaceCallback(Datum arg, int cacheid, ItemPointer tuplePtr);
static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
int **argnumbers);
/* These don't really need to appear in any header file */
Datum pg_table_is_visible(PG_FUNCTION_ARGS);
@ -562,8 +565,14 @@ 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. (expand_variadic and expand_defaults must be
* false in this case.)
* regardless of argument count. (argnames must be NIL, and expand_variadic
* and expand_defaults must be false, in this case.)
*
* If argnames isn't NIL, we are considering a named- or mixed-notation call,
* and only functions having all the listed argument names will be returned.
* (We assume that length(argnames) <= nargs and all the passed-in names are
* distinct.) The returned structs will include an argnumbers array showing
* the actual argument index for each logical argument position.
*
* If expand_variadic is true, then variadic functions having the same number
* or fewer arguments will be retrieved, with the variadic argument and any
@ -583,6 +592,13 @@ TypeIsVisible(Oid typid)
* than nargs arguments while the variadic transformation requires the same
* number or less.
*
* When argnames isn't NIL, the returned args[] type arrays are not ordered
* according to the functions' declarations, but rather according to the call:
* first any positional arguments, then the named arguments, then defaulted
* arguments (if needed and allowed by expand_defaults). The argnumbers[]
* array can be used to map this back to the catalog information.
* argnumbers[k] is set to the proargtypes index of the k'th call argument.
*
* We search a single namespace if the function name is qualified, else
* all namespaces in the search path. In the multiple-namespace case,
* we arrange for entries in earlier namespaces to mask identical entries in
@ -596,15 +612,16 @@ TypeIsVisible(Oid typid)
* It is guaranteed that the return list will never contain multiple entries
* with identical argument lists. When expand_defaults is true, the entries
* could have more than nargs positions, but we still guarantee that they are
* distinct in the first nargs positions. However, if either expand_variadic
* or expand_defaults is true, there might be multiple candidate functions
* that expand to identical argument lists. Rather than throw error here,
* we report such situations by setting oid = 0 in the ambiguous entries.
* distinct in the first nargs positions. However, if argnames isn't NIL or
* either expand_variadic or expand_defaults is true, there might be multiple
* candidate functions that expand to identical argument lists. Rather than
* throw error here, we report such situations by returning a single entry
* with oid = 0 that represents a set of such conflicting candidates.
* The caller might end up discarding such an entry anyway, but if it selects
* such an entry it should react as though the call were ambiguous.
*/
FuncCandidateList
FuncnameGetCandidates(List *names, int nargs,
FuncnameGetCandidates(List *names, int nargs, List *argnames,
bool expand_variadic, bool expand_defaults)
{
FuncCandidateList resultList = NULL;
@ -648,42 +665,9 @@ FuncnameGetCandidates(List *names, int nargs,
bool variadic;
bool use_defaults;
Oid va_elem_type;
int *argnumbers = NULL;
FuncCandidateList newResult;
/*
* Check if function is variadic, and get variadic element type if so.
* If expand_variadic is false, we should just ignore variadic-ness.
*/
if (pronargs <= nargs && expand_variadic)
{
va_elem_type = procform->provariadic;
variadic = OidIsValid(va_elem_type);
any_special |= variadic;
}
else
{
va_elem_type = InvalidOid;
variadic = false;
}
/*
* Check if function can match by using parameter defaults.
*/
if (pronargs > nargs && expand_defaults)
{
/* Ignore if not enough default expressions */
if (nargs + procform->pronargdefaults < pronargs)
continue;
use_defaults = true;
any_special = true;
}
else
use_defaults = false;
/* Ignore if it doesn't match requested argument count */
if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
continue;
if (OidIsValid(namespaceId))
{
/* Consider only procs in specified namespace */
@ -709,6 +693,88 @@ FuncnameGetCandidates(List *names, int nargs,
continue; /* proc is not in search path */
}
if (argnames != NIL)
{
/*
* Call uses named or mixed notation
*
* Named or mixed notation can match a variadic function only
* if expand_variadic is off; otherwise there is no way to match
* the presumed-nameless parameters expanded from the variadic
* array.
*/
if (OidIsValid(procform->provariadic) && expand_variadic)
continue;
va_elem_type = InvalidOid;
variadic = false;
/*
* Check argument count.
*/
Assert(nargs >= 0); /* -1 not supported with argnames */
if (pronargs > nargs && expand_defaults)
{
/* Ignore if not enough default expressions */
if (nargs + procform->pronargdefaults < pronargs)
continue;
use_defaults = true;
}
else
use_defaults = false;
/* Ignore if it doesn't match requested argument count */
if (pronargs != nargs && !use_defaults)
continue;
/* Check for argument name match, generate positional mapping */
if (!MatchNamedCall(proctup, nargs, argnames,
&argnumbers))
continue;
/* Named argument matching is always "special" */
any_special = true;
}
else
{
/*
* Call uses positional notation
*
* Check if function is variadic, and get variadic element type if
* so. If expand_variadic is false, we should just ignore
* variadic-ness.
*/
if (pronargs <= nargs && expand_variadic)
{
va_elem_type = procform->provariadic;
variadic = OidIsValid(va_elem_type);
any_special |= variadic;
}
else
{
va_elem_type = InvalidOid;
variadic = false;
}
/*
* Check if function can match by using parameter defaults.
*/
if (pronargs > nargs && expand_defaults)
{
/* Ignore if not enough default expressions */
if (nargs + procform->pronargdefaults < pronargs)
continue;
use_defaults = true;
any_special = true;
}
else
use_defaults = false;
/* Ignore if it doesn't match requested argument count */
if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
continue;
}
/*
* 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
@ -722,8 +788,22 @@ FuncnameGetCandidates(List *names, int nargs,
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = effective_nargs;
memcpy(newResult->args, procform->proargtypes.values,
pronargs * sizeof(Oid));
newResult->argnumbers = argnumbers;
if (argnumbers)
{
/* Re-order the argument types into call's logical order */
Oid *proargtypes = procform->proargtypes.values;
int i;
for (i = 0; i < pronargs; i++)
newResult->args[i] = proargtypes[argnumbers[i]];
}
else
{
/* Simple positional case, just copy proargtypes as-is */
memcpy(newResult->args, procform->proargtypes.values,
pronargs * sizeof(Oid));
}
if (variadic)
{
int i;
@ -741,9 +821,9 @@ FuncnameGetCandidates(List *names, int nargs,
* Does it have the same arguments as something we already accepted?
* If so, decide what to do to avoid returning duplicate argument
* lists. We can skip this check for the single-namespace case if no
* special (variadic or defaults) match has been made, since then the
* unique index on pg_proc guarantees all the matches have different
* argument lists.
* special (named, variadic or defaults) match has been made, since
* then the unique index on pg_proc guarantees all the matches have
* different argument lists.
*/
if (resultList != NULL &&
(any_special || !OidIsValid(namespaceId)))
@ -827,7 +907,8 @@ FuncnameGetCandidates(List *names, int nargs,
* both foo(numeric, variadic numeric[]) and
* foo(variadic numeric[]) in the same namespace, or
* both foo(int) and foo (int, int default something)
* in the same namespace.
* in the same namespace, or both foo(a int, b text)
* and foo(b text, a int) in the same namespace.
*----------
*/
preference = 0;
@ -885,6 +966,125 @@ FuncnameGetCandidates(List *names, int nargs,
return resultList;
}
/*
* MatchNamedCall
* Given a pg_proc heap tuple and a call's list of argument names,
* check whether the function could match the call.
*
* The call could match if all supplied argument names are accepted by
* the function, in positions after the last positional argument, and there
* are defaults for all unsupplied arguments.
*
* The number of positional arguments is nargs - list_length(argnames).
* Note caller has already done basic checks on argument count.
*
* On match, return true and fill *argnumbers with a palloc'd array showing
* the mapping from call argument positions to actual function argument
* numbers. Defaulted arguments are included in this map, at positions
* after the last supplied argument.
*/
static bool
MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
int **argnumbers)
{
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
int pronargs = procform->pronargs;
int numposargs = nargs - list_length(argnames);
int pronallargs;
Oid *p_argtypes;
char **p_argnames;
char *p_argmodes;
bool arggiven[FUNC_MAX_ARGS];
bool isnull;
int ap; /* call args position */
int pp; /* proargs position */
ListCell *lc;
Assert(argnames != NIL);
Assert(numposargs >= 0);
Assert(nargs <= pronargs);
/* Ignore this function if its proargnames is null */
(void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargnames,
&isnull);
if (isnull)
return false;
/* OK, let's extract the argument names and types */
pronallargs = get_func_arg_info(proctup,
&p_argtypes, &p_argnames, &p_argmodes);
Assert(p_argnames != NULL);
/* initialize state for matching */
*argnumbers = (int *) palloc(pronargs * sizeof(int));
memset(arggiven, false, pronargs * sizeof(bool));
/* there are numposargs positional args before the named args */
for (ap = 0; ap < numposargs; ap++)
{
(*argnumbers)[ap] = ap;
arggiven[ap] = true;
}
/* now examine the named args */
foreach(lc, argnames)
{
char *argname = (char *) lfirst(lc);
bool found;
int i;
pp = 0;
found = false;
for (i = 0; i < pronallargs; i++)
{
/* consider only input parameters */
if (p_argmodes &&
(p_argmodes[i] != FUNC_PARAM_IN &&
p_argmodes[i] != FUNC_PARAM_INOUT &&
p_argmodes[i] != FUNC_PARAM_VARIADIC))
continue;
if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0)
{
/* fail if argname matches a positional argument */
if (arggiven[pp])
return false;
arggiven[pp] = true;
(*argnumbers)[ap] = pp;
found = true;
break;
}
/* increase pp only for input parameters */
pp++;
}
/* if name isn't in proargnames, fail */
if (!found)
return false;
ap++;
}
Assert(ap == nargs); /* processed all actual parameters */
/* Check for default arguments */
if (nargs < pronargs)
{
int first_arg_with_default = pronargs - procform->pronargdefaults;
for (pp = numposargs; pp < pronargs; pp++)
{
if (arggiven[pp])
continue;
/* fail if arg not given and no default available */
if (pp < first_arg_with_default)
return false;
(*argnumbers)[ap++] = pp;
}
}
Assert(ap == pronargs); /* processed all function parameters */
return true;
}
/*
* FunctionIsVisible
* Determine whether a function (identified by OID) is visible in the
@ -932,7 +1132,7 @@ FunctionIsVisible(Oid funcid)
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
nargs, false, false);
nargs, NIL, false, false);
for (; clist; clist = clist->next)
{
@ -1202,6 +1402,7 @@ OpernameGetCandidates(List *names, char oprkind)
newResult->nargs = 2;
newResult->nvargs = 0;
newResult->ndargs = 0;
newResult->argnumbers = NULL;
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.102 2009/06/11 14:48:55 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.103 2009/10/08 02:39:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -321,7 +321,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, false, false,
fdresult = func_get_detail(fnName, NIL, NIL,
nargs, input_types, false, false,
&fnOid, rettype, &retset, &nvargs,
&true_oid_array, NULL);
@ -330,12 +331,14 @@ lookup_agg_function(List *fnName,
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(fnName, nargs, input_types))));
func_signature_string(fnName, nargs,
NIL, input_types))));
if (retset)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s returns a set",
func_signature_string(fnName, nargs, input_types))));
func_signature_string(fnName, nargs,
NIL, input_types))));
/*
* If there are any polymorphic types involved, enforce consistency, and
@ -359,7 +362,8 @@ lookup_agg_function(List *fnName,
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s requires run-time type coercion",
func_signature_string(fnName, nargs, true_oid_array))));
func_signature_string(fnName, nargs,
NIL, true_oid_array))));
}
/* Check aggregate creator has permission to call the function */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.167 2009/10/05 19:24:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.168 2009/10/08 02:39:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -348,6 +348,8 @@ ProcedureCreate(const char *procedureName,
{
/* There is one; okay to replace it? */
Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
Datum proargnames;
bool isnull;
if (!replace)
ereport(ERROR,
@ -393,6 +395,49 @@ ProcedureCreate(const char *procedureName,
errhint("Use DROP FUNCTION first.")));
}
/*
* If there were any named input parameters, check to make sure the
* names have not been changed, as this could break existing calls.
* We allow adding names to formerly unnamed parameters, though.
*/
proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
Anum_pg_proc_proargnames,
&isnull);
if (!isnull)
{
Datum proargmodes;
char **old_arg_names;
char **new_arg_names;
int n_old_arg_names;
int n_new_arg_names;
int j;
proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
Anum_pg_proc_proargmodes,
&isnull);
if (isnull)
proargmodes = PointerGetDatum(NULL); /* just to be sure */
n_old_arg_names = get_func_input_arg_names(proargnames,
proargmodes,
&old_arg_names);
n_new_arg_names = get_func_input_arg_names(parameterNames,
parameterModes,
&new_arg_names);
for (j = 0; j < n_old_arg_names; j++)
{
if (old_arg_names[j] == NULL)
continue;
if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
strcmp(old_arg_names[j], new_arg_names[j]) != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot change name of input parameter \"%s\"",
old_arg_names[j]),
errhint("Use DROP FUNCTION first.")));
}
}
/*
* If there are existing defaults, check compatibility: redefinition
* must not remove any defaults nor change their types. (Removing a
@ -404,7 +449,6 @@ ProcedureCreate(const char *procedureName,
if (oldproc->pronargdefaults != 0)
{
Datum proargdefaults;
bool isnull;
List *oldDefaults;
ListCell *oldlc;
ListCell *newlc;

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.49 2009/06/11 14:48:55 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.50 2009/10/08 02:39:18 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -297,6 +297,7 @@ RenameAggregate(List *name, List *args, const char *newname)
errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname,
procForm->pronargs,
NIL,
procForm->proargtypes.values),
get_namespace_name(namespaceOid))));

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.111 2009/09/22 23:43:37 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.112 2009/10/08 02:39:19 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@ -285,6 +285,39 @@ examine_parameter_list(List *parameters, Oid languageOid,
if (fp->name && fp->name[0])
{
ListCell *px;
/*
* As of Postgres 8.5 we disallow using the same name for two
* input or two output function parameters. Depending on the
* function's language, conflicting input and output names might
* be bad too, but we leave it to the PL to complain if so.
*/
foreach(px, parameters)
{
FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
if (prevfp == fp)
break;
/* pure in doesn't conflict with pure out */
if ((fp->mode == FUNC_PARAM_IN ||
fp->mode == FUNC_PARAM_VARIADIC) &&
(prevfp->mode == FUNC_PARAM_OUT ||
prevfp->mode == FUNC_PARAM_TABLE))
continue;
if ((prevfp->mode == FUNC_PARAM_IN ||
prevfp->mode == FUNC_PARAM_VARIADIC) &&
(fp->mode == FUNC_PARAM_OUT ||
fp->mode == FUNC_PARAM_TABLE))
continue;
if (prevfp->name && prevfp->name[0] &&
strcmp(prevfp->name, fp->name) == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("parameter name \"%s\" used more than once",
fp->name)));
}
paramNames[i] = CStringGetTextDatum(fp->name);
have_names = true;
}
@ -1097,6 +1130,7 @@ RenameFunction(List *name, List *argtypes, const char *newname)
errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname,
procForm->pronargs,
NIL,
procForm->proargtypes.values),
get_namespace_name(namespaceOid))));
}

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.17 2009/06/11 14:48:56 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.18 2009/10/08 02:39:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -107,7 +107,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("function %s should return type %s",
func_signature_string(funcName, nargs, typeId),
func_signature_string(funcName, nargs, NIL, typeId),
format_type_be(retTypeId))));
return ObjectIdGetDatum(procOid);
@ -945,7 +945,7 @@ get_ts_template_func(DefElem *defel, int attnum)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("function %s should return type %s",
func_signature_string(funcName, nargs, typeId),
func_signature_string(funcName, nargs, NIL, typeId),
format_type_be(retTypeId))));
return ObjectIdGetDatum(procOid);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.137 2009/07/30 02:45:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.138 2009/10/08 02:39:19 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -1267,7 +1267,7 @@ findTypeInputFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */
}
@ -1318,7 +1318,7 @@ findTypeOutputFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */
}
@ -1349,7 +1349,7 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */
}
@ -1372,7 +1372,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */
}
@ -1393,7 +1393,7 @@ findTypeTypmodinFunction(List *procname)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
func_signature_string(procname, 1, NIL, argList))));
if (get_func_rettype(procOid) != INT4OID)
ereport(ERROR,
@ -1420,7 +1420,7 @@ findTypeTypmodoutFunction(List *procname)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
func_signature_string(procname, 1, NIL, argList))));
if (get_func_rettype(procOid) != CSTRINGOID)
ereport(ERROR,
@ -1447,7 +1447,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
func_signature_string(procname, 1, NIL, argList))));
if (get_func_rettype(procOid) != BOOLOID)
ereport(ERROR,

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.441 2009/10/07 22:14:20 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.442 2009/10/08 02:39:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1019,6 +1019,22 @@ _copyFuncExpr(FuncExpr *from)
return newnode;
}
/*
* _copyNamedArgExpr *
*/
static NamedArgExpr *
_copyNamedArgExpr(NamedArgExpr *from)
{
NamedArgExpr *newnode = makeNode(NamedArgExpr);
COPY_NODE_FIELD(arg);
COPY_STRING_FIELD(name);
COPY_SCALAR_FIELD(argnumber);
COPY_LOCATION_FIELD(location);
return newnode;
}
/*
* _copyOpExpr
*/
@ -3587,6 +3603,9 @@ copyObject(void *from)
case T_FuncExpr:
retval = _copyFuncExpr(from);
break;
case T_NamedArgExpr:
retval = _copyNamedArgExpr(from);
break;
case T_OpExpr:
retval = _copyOpExpr(from);
break;

View File

@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.364 2009/10/07 22:14:20 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.365 2009/10/08 02:39:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -241,6 +241,17 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
return true;
}
static bool
_equalNamedArgExpr(NamedArgExpr *a, NamedArgExpr *b)
{
COMPARE_NODE_FIELD(arg);
COMPARE_STRING_FIELD(name);
COMPARE_SCALAR_FIELD(argnumber);
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool
_equalOpExpr(OpExpr *a, OpExpr *b)
{
@ -2375,6 +2386,9 @@ equal(void *a, void *b)
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
break;
case T_NamedArgExpr:
retval = _equalNamedArgExpr(a, b);
break;
case T_OpExpr:
retval = _equalOpExpr(a, b);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.42 2009/07/30 02:45:37 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.43 2009/10/08 02:39:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -69,6 +69,9 @@ exprType(Node *expr)
case T_FuncExpr:
type = ((FuncExpr *) expr)->funcresulttype;
break;
case T_NamedArgExpr:
type = exprType((Node *) ((NamedArgExpr *) expr)->arg);
break;
case T_OpExpr:
type = ((OpExpr *) expr)->opresulttype;
break;
@ -259,6 +262,8 @@ exprTypmod(Node *expr)
return coercedTypmod;
}
break;
case T_NamedArgExpr:
return exprTypmod((Node *) ((NamedArgExpr *) expr)->arg);
case T_SubLink:
{
SubLink *sublink = (SubLink *) expr;
@ -676,6 +681,15 @@ exprLocation(Node *expr)
exprLocation((Node *) fexpr->args));
}
break;
case T_NamedArgExpr:
{
NamedArgExpr *na = (NamedArgExpr *) expr;
/* consider both argument name and value */
loc = leftmostLoc(na->location,
exprLocation((Node *) na->arg));
}
break;
case T_OpExpr:
case T_DistinctExpr: /* struct-equivalent to OpExpr */
case T_NullIfExpr: /* struct-equivalent to OpExpr */
@ -1117,6 +1131,8 @@ expression_tree_walker(Node *node,
return true;
}
break;
case T_NamedArgExpr:
return walker(((NamedArgExpr *) node)->arg, context);
case T_OpExpr:
{
OpExpr *expr = (OpExpr *) node;
@ -1623,6 +1639,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
case T_NamedArgExpr:
{
NamedArgExpr *nexpr = (NamedArgExpr *) node;
NamedArgExpr *newnode;
FLATCOPY(newnode, nexpr, NamedArgExpr);
MUTATE(newnode->arg, nexpr->arg, Expr *);
return (Node *) newnode;
}
break;
case T_OpExpr:
{
OpExpr *expr = (OpExpr *) node;
@ -2382,6 +2408,8 @@ bool
/* function name is deemed uninteresting */
}
break;
case T_NamedArgExpr:
return walker(((NamedArgExpr *) node)->arg, context);
case T_A_Indices:
{
A_Indices *indices = (A_Indices *) node;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.365 2009/10/06 00:55:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.366 2009/10/08 02:39:21 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -875,6 +875,17 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
WRITE_LOCATION_FIELD(location);
}
static void
_outNamedArgExpr(StringInfo str, NamedArgExpr *node)
{
WRITE_NODE_TYPE("NAMEDARGEXPR");
WRITE_NODE_FIELD(arg);
WRITE_STRING_FIELD(name);
WRITE_INT_FIELD(argnumber);
WRITE_LOCATION_FIELD(location);
}
static void
_outOpExpr(StringInfo str, OpExpr *node)
{
@ -2514,6 +2525,9 @@ _outNode(StringInfo str, void *obj)
case T_FuncExpr:
_outFuncExpr(str, obj);
break;
case T_NamedArgExpr:
_outNamedArgExpr(str, obj);
break;
case T_OpExpr:
_outOpExpr(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.223 2009/07/16 06:33:42 petere Exp $
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.224 2009/10/08 02:39:21 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@ -525,6 +525,22 @@ _readFuncExpr(void)
READ_DONE();
}
/*
* _readNamedArgExpr
*/
static NamedArgExpr *
_readNamedArgExpr(void)
{
READ_LOCALS(NamedArgExpr);
READ_NODE_FIELD(arg);
READ_STRING_FIELD(name);
READ_INT_FIELD(argnumber);
READ_LOCATION_FIELD(location);
READ_DONE();
}
/*
* _readOpExpr
*/
@ -1207,6 +1223,8 @@ parseNodeString(void)
return_value = _readArrayRef();
else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr();
else if (MATCH("NAMEDARGEXPR", 12))
return_value = _readNamedArgExpr();
else if (MATCH("OPEXPR", 6))
return_value = _readOpExpr();
else if (MATCH("DISTINCTEXPR", 12))

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.256 2009/06/11 14:48:59 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.257 2009/10/08 02:39:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -526,7 +526,8 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
/*
* Simplify constant expressions.
*
* Note: one essential effect here is to insert the current actual values
* Note: an essential effect of this is to convert named-argument function
* calls to positional notation and insert the current actual values
* of any default arguments for functions. To ensure that happens, we
* *must* process all expressions here. Previous PG versions sometimes
* skipped const-simplification if it didn't seem worth the trouble, but
@ -2658,9 +2659,10 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
* Currently, we disallow sublinks in standalone expressions, so there's no
* real "planning" involved here. (That might not always be true though.)
* What we must do is run eval_const_expressions to ensure that any function
* default arguments get inserted. The fact that constant subexpressions
* get simplified is a side-effect that is useful when the expression will
* get evaluated more than once. Also, we must fix operator function IDs.
* calls are converted to positional notation and function default arguments
* get inserted. The fact that constant subexpressions get simplified is a
* side-effect that is useful when the expression will get evaluated more than
* once. Also, we must fix operator function IDs.
*
* Note: this must not make any damaging changes to the passed-in expression
* tree. (It would actually be okay to apply fix_opfuncids to it, but since
@ -2672,7 +2674,10 @@ expression_planner(Expr *expr)
{
Node *result;
/* Insert default arguments and simplify constant subexprs */
/*
* Convert named-argument function calls, insert default arguments and
* simplify constant subexprs
*/
result = eval_const_expressions(NULL, (Node *) expr);
/* Fill in opfuncid values if missing */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.278 2009/07/20 00:24:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.279 2009/10/08 02:39:21 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -95,11 +95,18 @@ static List *simplify_and_arguments(List *args,
static Expr *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod, List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context);
static List *reorder_function_arguments(List *args, Oid result_type,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static List *add_function_defaults(List *args, Oid result_type,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static List *fetch_function_defaults(HeapTuple func_tuple);
static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
HeapTuple func_tuple,
@ -2003,7 +2010,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
* OR clauses into N-argument form. See comments in prepqual.c.
*
* NOTE: another critical effect is that any function calls that require
* default arguments will be expanded.
* default arguments will be expanded, and named-argument calls will be
* converted to positional notation. The executor won't handle either.
*--------------------
*/
Node *
@ -2113,17 +2121,26 @@ eval_const_expressions_mutator(Node *node,
{
FuncExpr *expr = (FuncExpr *) node;
List *args;
bool has_named_args;
Expr *simple;
FuncExpr *newexpr;
ListCell *lc;
/*
* Reduce constants in the FuncExpr's arguments. We know args is
* either NIL or a List node, so we can call expression_tree_mutator
* directly rather than recursing to self.
* Reduce constants in the FuncExpr's arguments, and check to see
* if there are any named args.
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
eval_const_expressions_mutator,
(void *) context);
args = NIL;
has_named_args = false;
foreach(lc, expr->args)
{
Node *arg = (Node *) lfirst(lc);
arg = eval_const_expressions_mutator(arg, context);
if (IsA(arg, NamedArgExpr))
has_named_args = true;
args = lappend(args, arg);
}
/*
* Code for op/func reduction is pretty bulky, so split it out as a
@ -2134,14 +2151,15 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
&args,
true, context);
has_named_args, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
/*
* The expression cannot be simplified any further, so build and
* return a replacement FuncExpr node using the possibly-simplified
* arguments.
* arguments. Note that we have also converted the argument list
* to positional notation.
*/
newexpr = makeNode(FuncExpr);
newexpr->funcid = expr->funcid;
@ -2181,7 +2199,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
true, context);
false, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
@ -2274,7 +2292,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
false, context);
false, false, context);
if (simple) /* successfully simplified it */
{
/*
@ -2466,7 +2484,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(outfunc,
CSTRINGOID, -1,
&args,
true, context);
false, true, context);
if (simple) /* successfully simplified output fn */
{
/*
@ -2484,7 +2502,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(infunc,
expr->resulttype, -1,
&args,
true, context);
false, true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
@ -3241,15 +3259,16 @@ simplify_boolean_equality(Oid opno, List *args)
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function call.
*
* This function is also responsible for adding any default argument
* expressions onto the function argument list; which is a bit grotty,
* but it avoids an extra fetch of the function's pg_proc tuple. For this
* reason, the args list is pass-by-reference, and it may get modified
* even if simplification fails.
* This function is also responsible for converting named-notation argument
* lists into positional notation and/or adding any needed default argument
* expressions; which is a bit grotty, but it avoids an extra fetch of the
* function's pg_proc tuple. For this reason, the args list is
* pass-by-reference, and it may get modified even if simplification fails.
*/
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context)
{
@ -3270,8 +3289,14 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
/* While we have the tuple, check if we need to add defaults */
if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
/*
* While we have the tuple, reorder named arguments and add default
* arguments if needed.
*/
if (has_named_args)
*args = reorder_function_arguments(*args, result_type, func_tuple,
context);
else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
*args = add_function_defaults(*args, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
@ -3286,13 +3311,113 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
return newexpr;
}
/*
* reorder_function_arguments: convert named-notation args to positional args
*
* This function also inserts default argument values as needed, since it's
* impossible to form a truly valid positional call without that.
*/
static List *
reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int pronargs = funcform->pronargs;
int nargsprovided = list_length(args);
Node *argarray[FUNC_MAX_ARGS];
Bitmapset *defargnumbers;
ListCell *lc;
int i;
Assert(nargsprovided <= pronargs);
if (pronargs > FUNC_MAX_ARGS)
elog(ERROR, "too many function arguments");
MemSet(argarray, 0, pronargs * sizeof(Node *));
/* Deconstruct the argument list into an array indexed by argnumber */
i = 0;
foreach(lc, args)
{
Node *arg = (Node *) lfirst(lc);
if (!IsA(arg, NamedArgExpr))
{
/* positional argument, assumed to precede all named args */
Assert(argarray[i] == NULL);
argarray[i++] = arg;
}
else
{
NamedArgExpr *na = (NamedArgExpr *) arg;
Assert(argarray[na->argnumber] == NULL);
argarray[na->argnumber] = (Node *) na->arg;
}
}
/*
* Fetch default expressions, if needed, and insert into array at
* proper locations (they aren't necessarily consecutive or all used)
*/
defargnumbers = NULL;
if (nargsprovided < pronargs)
{
List *defaults = fetch_function_defaults(func_tuple);
i = pronargs - funcform->pronargdefaults;
foreach(lc, defaults)
{
if (argarray[i] == NULL)
{
argarray[i] = (Node *) lfirst(lc);
defargnumbers = bms_add_member(defargnumbers, i);
}
i++;
}
}
/* Now reconstruct the args list in proper order */
args = NIL;
for (i = 0; i < pronargs; i++)
{
Assert(argarray[i] != NULL);
args = lappend(args, argarray[i]);
}
/* Recheck argument types and add casts if needed */
recheck_cast_function_args(args, result_type, func_tuple);
/*
* Lastly, we have to recursively simplify the defaults we just added
* (but don't recurse on the args passed in, as we already did those).
* This isn't merely an optimization, it's *necessary* since there could
* be functions with named or defaulted arguments down in there.
*
* Note that we do this last in hopes of simplifying any typecasts that
* were added by recheck_cast_function_args --- there shouldn't be any new
* casts added to the explicit arguments, but casts on the defaults are
* possible.
*/
if (defargnumbers != NULL)
{
i = 0;
foreach(lc, args)
{
if (bms_is_member(i, defargnumbers))
lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
context);
i++;
}
}
return args;
}
/*
* add_function_defaults: add missing function arguments from its defaults
*
* It is possible for some of the defaulted arguments to be polymorphic;
* therefore we can't assume that the default expressions have the correct
* data types already. We have to re-resolve polymorphics and do coercion
* just like the parser did.
* This is used only when the argument list was positional to begin with,
* and so we know we just need to add defaults at the end.
*/
static List *
add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
@ -3300,16 +3425,58 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int nargsprovided = list_length(args);
List *defaults;
int ndelete;
ListCell *lc;
/* Get all the default expressions from the pg_proc tuple */
defaults = fetch_function_defaults(func_tuple);
/* Delete any unused defaults from the list */
ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
if (ndelete < 0)
elog(ERROR, "not enough default arguments");
while (ndelete-- > 0)
defaults = list_delete_first(defaults);
/* And form the combined argument list */
args = list_concat(args, defaults);
/* Recheck argument types and add casts if needed */
recheck_cast_function_args(args, result_type, func_tuple);
/*
* Lastly, we have to recursively simplify the defaults we just added
* (but don't recurse on the args passed in, as we already did those).
* This isn't merely an optimization, it's *necessary* since there could
* be functions with named or defaulted arguments down in there.
*
* Note that we do this last in hopes of simplifying any typecasts that
* were added by recheck_cast_function_args --- there shouldn't be any new
* casts added to the explicit arguments, but casts on the defaults are
* possible.
*/
foreach(lc, args)
{
if (nargsprovided-- > 0)
continue; /* skip original arg positions */
lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
context);
}
return args;
}
/*
* fetch_function_defaults: get function's default arguments as expression list
*/
static List *
fetch_function_defaults(HeapTuple func_tuple)
{
List *defaults;
Datum proargdefaults;
bool isnull;
char *str;
List *defaults;
int ndelete;
int nargs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid declared_arg_types[FUNC_MAX_ARGS];
Oid rettype;
ListCell *lc;
/* The error cases here shouldn't happen, but check anyway */
proargdefaults = SysCacheGetAttr(PROCOID, func_tuple,
@ -3321,20 +3488,33 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
/* Delete any unused defaults from the list */
ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
if (ndelete < 0)
elog(ERROR, "not enough default arguments");
while (ndelete-- > 0)
defaults = list_delete_first(defaults);
/* And form the combined argument list */
args = list_concat(args, defaults);
Assert(list_length(args) == funcform->pronargs);
return defaults;
}
/*
* recheck_cast_function_args: recheck function args and typecast as needed
* after adding defaults.
*
* It is possible for some of the defaulted arguments to be polymorphic;
* therefore we can't assume that the default expressions have the correct
* data types already. We have to re-resolve polymorphics and do coercion
* just like the parser did.
*
* This should be a no-op if there are no polymorphic arguments,
* but we do it anyway to be sure.
*
* Note: if any casts are needed, the args list is modified in-place.
*/
static void
recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int nargs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid declared_arg_types[FUNC_MAX_ARGS];
Oid rettype;
ListCell *lc;
/*
* The next part should be a no-op if there are no polymorphic arguments,
* but we do it anyway to be sure.
*/
if (list_length(args) > FUNC_MAX_ARGS)
elog(ERROR, "too many function arguments");
nargs = 0;
@ -3342,6 +3522,7 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
{
actual_arg_types[nargs++] = exprType((Node *) lfirst(lc));
}
Assert(nargs == funcform->pronargs);
memcpy(declared_arg_types, funcform->proargtypes.values,
funcform->pronargs * sizeof(Oid));
rettype = enforce_generic_type_consistency(actual_arg_types,
@ -3355,22 +3536,6 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
/* perform any necessary typecasting of arguments */
make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types);
/*
* Lastly, we have to recursively simplify the arguments we just added
* (but don't recurse on the ones passed in, as we already did those).
* This isn't merely an optimization, it's *necessary* since there could
* be functions with defaulted arguments down in there.
*/
foreach(lc, args)
{
if (nargsprovided-- > 0)
continue; /* skip original arg positions */
lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
context);
}
return args;
}
/*
@ -3916,6 +4081,7 @@ Query *
inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
{
FuncExpr *fexpr;
Oid func_oid;
HeapTuple func_tuple;
Form_pg_proc funcform;
Oid *argtypes;
@ -3944,6 +4110,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
fexpr = (FuncExpr *) rte->funcexpr;
if (fexpr == NULL || !IsA(fexpr, FuncExpr))
return NULL;
func_oid = fexpr->funcid;
/*
* The function must be declared to return a set, else inlining would
@ -3967,17 +4134,17 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
return NULL;
/* Check permission to call function (fail later, if not) */
if (pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
if (pg_proc_aclcheck(func_oid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
return NULL;
/*
* OK, let's take a look at the function's pg_proc entry.
*/
func_tuple = SearchSysCache(PROCOID,
ObjectIdGetDatum(fexpr->funcid),
ObjectIdGetDatum(func_oid),
0, 0, 0);
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
elog(ERROR, "cache lookup failed for function %u", func_oid);
funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
/*
@ -3985,16 +4152,15 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* properties. In particular it mustn't be declared STRICT, since we
* couldn't enforce that. It also mustn't be VOLATILE, because that is
* supposed to cause it to be executed with its own snapshot, rather than
* sharing the snapshot of the calling query. (The nargs check is just
* paranoia, ditto rechecking proretset.)
* sharing the snapshot of the calling query. (Rechecking proretset is
* just paranoia.)
*/
if (funcform->prolang != SQLlanguageId ||
funcform->proisstrict ||
funcform->provolatile == PROVOLATILE_VOLATILE ||
funcform->prosecdef ||
!funcform->proretset ||
!heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
funcform->pronargs != list_length(fexpr->args))
!heap_attisnull(func_tuple, Anum_pg_proc_proconfig))
{
ReleaseSysCache(func_tuple);
return NULL;
@ -4020,6 +4186,24 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(mycxt);
/*
* Run eval_const_expressions on the function call. This is necessary
* to ensure that named-argument notation is converted to positional
* notation and any default arguments are inserted. It's a bit of
* overkill for the arguments, since they'll get processed again later,
* but no harm will be done.
*/
fexpr = (FuncExpr *) eval_const_expressions(root, (Node *) fexpr);
/* It should still be a call of the same function, but let's check */
if (!IsA(fexpr, FuncExpr) ||
fexpr->funcid != func_oid)
goto fail;
/* Arg list length should now match the function */
if (list_length(fexpr->args) != funcform->pronargs)
goto fail;
/* Check for polymorphic arguments, and substitute actual arg types */
argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
memcpy(argtypes, funcform->proargtypes.values,
@ -4038,7 +4222,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
Anum_pg_proc_prosrc,
&isNull);
if (isNull)
elog(ERROR, "null prosrc for function %u", fexpr->funcid);
elog(ERROR, "null prosrc for function %u", func_oid);
src = TextDatumGetCString(tmp);
/*
@ -4076,7 +4260,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* shows it's returning a whole tuple result; otherwise what it's
* returning is a single composite column which is not what we need.
*/
if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype,
if (!check_sql_fn_retval(func_oid, fexpr->funcresulttype,
querytree_list,
true, NULL) &&
(get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE ||
@ -4116,7 +4300,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* Since there is now no trace of the function in the plan tree, we must
* explicitly record the plan's dependency on the function.
*/
record_plan_function_dependency(root->glob, fexpr->funcid);
record_plan_function_dependency(root->glob, func_oid);
return querytree;

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.681 2009/10/07 22:14:21 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.682 2009/10/08 02:39:22 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -354,6 +354,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <node> def_arg columnElem where_clause where_or_current_clause
a_expr b_expr c_expr func_expr AexprConst indirection_el
columnref in_expr having_clause func_table array_expr
%type <list> func_arg_list
%type <node> func_arg_expr
%type <list> row type_list array_expr_list
%type <node> case_expr case_arg when_clause case_default
%type <list> when_clause_list
@ -9055,7 +9057,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
| func_name '(' expr_list ')' over_clause
| func_name '(' func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@ -9067,7 +9069,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
| func_name '(' VARIADIC a_expr ')' over_clause
| func_name '(' VARIADIC func_arg_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@ -9079,7 +9081,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
| func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause
| func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@ -9091,7 +9093,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
| func_name '(' ALL expr_list ')' over_clause
| func_name '(' ALL func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@ -9107,7 +9109,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
| func_name '(' DISTINCT expr_list ')' over_clause
| func_name '(' DISTINCT func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@ -9830,6 +9832,32 @@ expr_list: a_expr
}
;
/* function arguments can have names */
func_arg_list: func_arg_expr
{
$$ = list_make1($1);
}
| func_arg_list ',' func_arg_expr
{
$$ = lappend($1, $3);
}
;
func_arg_expr: a_expr
{
$$ = $1;
}
| a_expr AS param_name
{
NamedArgExpr *na = makeNode(NamedArgExpr);
na->arg = (Expr *) $1;
na->name = $3;
na->argnumber = -1; /* until determined */
na->location = @3;
$$ = (Node *) na;
}
;
type_list: Typename { $$ = list_make1($1); }
| type_list ',' Typename { $$ = lappend($1, $3); }
;
@ -10296,10 +10324,27 @@ AexprConst: Iconst
t->location = @1;
$$ = makeStringConstCast($2, @2, t);
}
| func_name '(' expr_list ')' Sconst
| func_name '(' func_arg_list ')' Sconst
{
/* generic syntax with a type modifier */
TypeName *t = makeTypeNameFromNameList($1);
ListCell *lc;
/*
* We must use func_arg_list in the production to avoid
* reduce/reduce conflicts, but we don't actually wish
* to allow NamedArgExpr in this context.
*/
foreach(lc, $3)
{
NamedArgExpr *arg = (NamedArgExpr *) lfirst(lc);
if (IsA(arg, NamedArgExpr))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type modifier cannot have AS name"),
parser_errposition(arg->location)));
}
t->typmods = $3;
t->location = @1;
$$ = makeStringConstCast($5, @5, t);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.243 2009/09/09 03:32:52 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.244 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -228,6 +228,15 @@ transformExpr(ParseState *pstate, Node *expr)
result = transformFuncCall(pstate, (FuncCall *) expr);
break;
case T_NamedArgExpr:
{
NamedArgExpr *na = (NamedArgExpr *) expr;
na->arg = (Expr *) transformExpr(pstate, (Node *) na->arg);
result = expr;
break;
}
case T_SubLink:
result = transformSubLink(pstate, (SubLink *) expr);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.216 2009/06/11 14:49:00 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.217 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -70,6 +70,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
int nargsplusdefs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid *declared_arg_types;
List *argnames;
List *argdefaults;
Node *retval;
bool retset;
@ -117,6 +118,46 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
actual_arg_types[nargs++] = argtype;
}
/*
* Check for named arguments; if there are any, build a list of names.
*
* We allow mixed notation (some named and some not), but only with all
* the named parameters after all the unnamed ones. So the name list
* corresponds to the last N actual parameters and we don't need any
* extra bookkeeping to match things up.
*/
argnames = NIL;
foreach(l, fargs)
{
Node *arg = lfirst(l);
if (IsA(arg, NamedArgExpr))
{
NamedArgExpr *na = (NamedArgExpr *) arg;
ListCell *lc;
/* Reject duplicate arg names */
foreach(lc, argnames)
{
if (strcmp(na->name, (char *) lfirst(lc)) == 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("argument name \"%s\" used more than once",
na->name),
parser_errposition(pstate, na->location)));
}
argnames = lappend(argnames, na->name);
}
else
{
if (argnames != NIL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("positional argument cannot follow named argument"),
parser_errposition(pstate, exprLocation(arg))));
}
}
if (fargs)
{
first_arg = linitial(fargs);
@ -127,10 +168,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 or variadic decoration.
* wasn't any aggregate or variadic decoration, nor an argument name.
*/
if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
!func_variadic && list_length(funcname) == 1)
!func_variadic && argnames == NIL && list_length(funcname) == 1)
{
Oid argtype = actual_arg_types[0];
@ -156,12 +197,17 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* 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. 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. We may also have to deal with default arguments.
* the function.
*
* Note: for a named-notation or variadic function call, the reported
* "true" types aren't really what is in pg_proc: the types are reordered
* to match the given argument order of named arguments, and a variadic
* argument is replaced by a suitable number of copies of its element
* type. We'll fix up the variadic case below. We may also have to deal
* with default arguments.
*/
fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
fdresult = func_get_detail(funcname, fargs, argnames, nargs,
actual_arg_types,
!func_variadic, true,
&funcid, &rettype, &retset, &nvargs,
&declared_arg_types, &argdefaults);
@ -225,7 +271,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function %s is not unique",
func_signature_string(funcname, nargs,
func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
errhint("Could not choose a best candidate function. "
"You might need to add explicit type casts."),
@ -234,7 +280,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(funcname, nargs,
func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
errhint("No function matches the given name and argument types. "
"You might need to add explicit type casts."),
@ -353,6 +399,18 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("aggregates cannot return sets"),
parser_errposition(pstate, location)));
/*
* Currently it's not possible to define an aggregate with named
* arguments, so this case should be impossible. Check anyway
* because the planner and executor wouldn't cope with NamedArgExprs
* in an Aggref node.
*/
if (argnames != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("aggregates cannot use named arguments"),
parser_errposition(pstate, location)));
/* parse_agg.c does additional aggregate-specific processing */
transformAggregateCall(pstate, aggref);
@ -406,6 +464,17 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("window functions cannot return sets"),
parser_errposition(pstate, location)));
/*
* We might want to support this later, but for now reject it
* because the planner and executor wouldn't cope with NamedArgExprs
* in a WindowFunc node.
*/
if (argnames != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("window functions cannot use named arguments"),
parser_errposition(pstate, location)));
/* parse_agg.c does additional window-func-specific processing */
transformWindowFuncCall(pstate, wfunc, over);
@ -801,14 +870,29 @@ func_select_candidate(int nargs,
* 1) check for possible interpretation as a type coercion request
* 2) apply the ambiguous-function resolution rules
*
* Note: we rely primarily on nargs/argtypes as the argument description.
* Return values *funcid through *true_typeids receive info about the function.
* If argdefaults isn't NULL, *argdefaults receives a list of any default
* argument expressions that need to be added to the given arguments.
*
* When processing a named- or mixed-notation call (ie, fargnames isn't NIL),
* the returned true_typeids and argdefaults are ordered according to the
* call's argument ordering: first any positional arguments, then the named
* arguments, then defaulted arguments (if needed and allowed by
* expand_defaults). Some care is needed if this information is to be compared
* to the function's pg_proc entry, but in practice the caller can usually
* just work with the call's argument ordering.
*
* We rely primarily on fargnames/nargs/argtypes as the argument description.
* The actual expression node list is passed in fargs so that we can check
* for type coercion of a constant. Some callers pass fargs == NIL
* indicating they don't want that check made.
* for type coercion of a constant. Some callers pass fargs == NIL indicating
* they don't need that check made. Note also that when fargnames isn't NIL,
* the fargs list must be passed if the caller wants actual argument position
* information to be returned into the NamedArgExpr nodes.
*/
FuncDetailCode
func_get_detail(List *funcname,
List *fargs,
List *fargnames,
int nargs,
Oid *argtypes,
bool expand_variadic,
@ -833,7 +917,7 @@ func_get_detail(List *funcname,
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
raw_candidates = FuncnameGetCandidates(funcname, nargs,
raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
expand_variadic, expand_defaults);
/*
@ -884,7 +968,7 @@ func_get_detail(List *funcname,
* coerce_type can't handle, we'll cause infinite recursion between
* this module and coerce_type!
*/
if (nargs == 1 && fargs != NIL)
if (nargs == 1 && fargs != NIL && fargnames == NIL)
{
Oid targetType = FuncNameAsType(funcname);
@ -967,17 +1051,47 @@ func_get_detail(List *funcname,
FuncDetailCode result;
/*
* If expanding variadics or defaults, the "best candidate" might
* represent multiple equivalently good functions; treat this case as
* ambiguous.
* If processing named args or expanding variadics or defaults, the
* "best candidate" might represent multiple equivalently good
* functions; treat this case as ambiguous.
*/
if (!OidIsValid(best_candidate->oid))
return FUNCDETAIL_MULTIPLE;
/*
* We disallow VARIADIC with named arguments unless the last
* argument (the one with VARIADIC attached) actually matched the
* variadic parameter. This is mere pedantry, really, but some
* folks insisted.
*/
if (fargnames != NIL && !expand_variadic && nargs > 0 &&
best_candidate->argnumbers[nargs - 1] != nargs - 1)
return FUNCDETAIL_NOTFOUND;
*funcid = best_candidate->oid;
*nvargs = best_candidate->nvargs;
*true_typeids = best_candidate->args;
/*
* If processing named args, return actual argument positions into
* NamedArgExpr nodes in the fargs list. This is a bit ugly but not
* worth the extra notation needed to do it differently.
*/
if (best_candidate->argnumbers != NULL)
{
int i = 0;
ListCell *lc;
foreach(lc, fargs)
{
NamedArgExpr *na = (NamedArgExpr *) lfirst(lc);
if (IsA(na, NamedArgExpr))
na->argnumber = best_candidate->argnumbers[i];
i++;
}
}
ftup = SearchSysCache(PROCOID,
ObjectIdGetDatum(best_candidate->oid),
0, 0, 0);
@ -988,36 +1102,73 @@ func_get_detail(List *funcname,
*rettype = pform->prorettype;
*retset = pform->proretset;
/* fetch default args if caller wants 'em */
if (argdefaults)
if (argdefaults && best_candidate->ndargs > 0)
{
if (best_candidate->ndargs > 0)
Datum proargdefaults;
bool isnull;
char *str;
List *defaults;
/* shouldn't happen, FuncnameGetCandidates messed up */
if (best_candidate->ndargs > pform->pronargdefaults)
elog(ERROR, "not enough default arguments");
proargdefaults = SysCacheGetAttr(PROCOID, ftup,
Anum_pg_proc_proargdefaults,
&isnull);
Assert(!isnull);
str = TextDatumGetCString(proargdefaults);
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
/* Delete any unused defaults from the returned list */
if (best_candidate->argnumbers != NULL)
{
Datum proargdefaults;
bool isnull;
char *str;
List *defaults;
/*
* This is a bit tricky in named notation, since the supplied
* arguments could replace any subset of the defaults. We
* work by making a bitmapset of the argnumbers of defaulted
* arguments, then scanning the defaults list and selecting
* the needed items. (This assumes that defaulted arguments
* should be supplied in their positional order.)
*/
Bitmapset *defargnumbers;
int *firstdefarg;
List *newdefaults;
ListCell *lc;
int i;
defargnumbers = NULL;
firstdefarg = &best_candidate->argnumbers[best_candidate->nargs - best_candidate->ndargs];
for (i = 0; i < best_candidate->ndargs; i++)
defargnumbers = bms_add_member(defargnumbers,
firstdefarg[i]);
newdefaults = NIL;
i = pform->pronargs - pform->pronargdefaults;
foreach(lc, defaults)
{
if (bms_is_member(i, defargnumbers))
newdefaults = lappend(newdefaults, lfirst(lc));
i++;
}
Assert(list_length(newdefaults) == best_candidate->ndargs);
bms_free(defargnumbers);
*argdefaults = newdefaults;
}
else
{
/*
* Defaults for positional notation are lots easier;
* just remove any unwanted ones from the front.
*/
int ndelete;
/* shouldn't happen, FuncnameGetCandidates messed up */
if (best_candidate->ndargs > pform->pronargdefaults)
elog(ERROR, "not enough default arguments");
proargdefaults = SysCacheGetAttr(PROCOID, ftup,
Anum_pg_proc_proargdefaults,
&isnull);
Assert(!isnull);
str = TextDatumGetCString(proargdefaults);
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
/* Delete any unused defaults from the returned list */
ndelete = list_length(defaults) - best_candidate->ndargs;
while (ndelete-- > 0)
defaults = list_delete_first(defaults);
*argdefaults = defaults;
}
else
*argdefaults = NIL;
}
if (pform->proisagg)
result = FUNCDETAIL_AGGREGATE;
@ -1060,13 +1211,36 @@ make_fn_arguments(ParseState *pstate,
/* types don't match? then force coercion using a function call... */
if (actual_arg_types[i] != declared_arg_types[i])
{
lfirst(current_fargs) = coerce_type(pstate,
lfirst(current_fargs),
actual_arg_types[i],
declared_arg_types[i], -1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST,
-1);
Node *node = (Node *) lfirst(current_fargs);
/*
* If arg is a NamedArgExpr, coerce its input expr instead ---
* we want the NamedArgExpr to stay at the top level of the list.
*/
if (IsA(node, NamedArgExpr))
{
NamedArgExpr *na = (NamedArgExpr *) node;
node = coerce_type(pstate,
(Node *) na->arg,
actual_arg_types[i],
declared_arg_types[i], -1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST,
-1);
na->arg = (Expr *) node;
}
else
{
node = coerce_type(pstate,
node,
actual_arg_types[i],
declared_arg_types[i], -1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST,
-1);
lfirst(current_fargs) = node;
}
}
i++;
}
@ -1223,25 +1397,39 @@ unknown_attribute(ParseState *pstate, Node *relref, char *attname,
* Build a string representing a function name, including arg types.
* The result is something like "foo(integer)".
*
* If argnames isn't NIL, it is a list of C strings representing the actual
* arg names for the last N arguments. This must be considered part of the
* function signature too, when dealing with named-notation function calls.
*
* This is typically used in the construction of function-not-found error
* messages.
*/
const char *
funcname_signature_string(const char *funcname,
int nargs, const Oid *argtypes)
funcname_signature_string(const char *funcname, int nargs,
List *argnames, const Oid *argtypes)
{
StringInfoData argbuf;
int numposargs;
ListCell *lc;
int i;
initStringInfo(&argbuf);
appendStringInfo(&argbuf, "%s(", funcname);
numposargs = nargs - list_length(argnames);
lc = list_head(argnames);
for (i = 0; i < nargs; i++)
{
if (i)
appendStringInfoString(&argbuf, ", ");
appendStringInfoString(&argbuf, format_type_be(argtypes[i]));
if (i >= numposargs)
{
appendStringInfo(&argbuf, " AS %s", (char *) lfirst(lc));
lc = lnext(lc);
}
}
appendStringInfoChar(&argbuf, ')');
@ -1254,10 +1442,11 @@ funcname_signature_string(const char *funcname,
* As above, but function name is passed as a qualified name list.
*/
const char *
func_signature_string(List *funcname, int nargs, const Oid *argtypes)
func_signature_string(List *funcname, int nargs,
List *argnames, const Oid *argtypes)
{
return funcname_signature_string(NameListToString(funcname),
nargs, argtypes);
nargs, argnames, argtypes);
}
/*
@ -1276,7 +1465,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
FuncCandidateList clist;
clist = FuncnameGetCandidates(funcname, nargs, false, false);
clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
while (clist)
{
@ -1289,7 +1478,8 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(funcname, nargs, argtypes))));
func_signature_string(funcname, nargs,
NIL, argtypes))));
return InvalidOid;
}
@ -1401,8 +1591,8 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("aggregate %s does not exist",
func_signature_string(aggname,
argcount, argoids))));
func_signature_string(aggname, argcount,
NIL, argoids))));
}
/* Make sure it's an aggregate */
@ -1422,8 +1612,8 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("function %s is not an aggregate",
func_signature_string(aggname,
argcount, argoids))));
func_signature_string(aggname, argcount,
NIL, argoids))));
}
ReleaseSysCache(ftup);

View File

@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.110 2009/01/01 17:23:49 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.111 2009/10/08 02:39:23 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, false, false);
clist = FuncnameGetCandidates(names, -1, NIL, false, false);
if (clist == NULL)
ereport(ERROR,
@ -190,7 +190,7 @@ regprocout(PG_FUNCTION_ARGS)
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-1, false, false);
-1, NIL, false, false);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
@ -277,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS)
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
clist = FuncnameGetCandidates(names, nargs, false, false);
clist = FuncnameGetCandidates(names, nargs, NIL, false, false);
for (; clist; clist = clist->next)
{

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.306 2009/08/01 19:59:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.307 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -218,8 +218,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, List *namespaces);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes,
bool *is_variadic);
static char *generate_function_name(Oid funcid, int nargs, List *argnames,
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);
@ -558,7 +558,8 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
appendStringInfo(&buf, "FOR EACH STATEMENT ");
appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
generate_function_name(trigrec->tgfoid, 0, NULL, NULL));
generate_function_name(trigrec->tgfoid, 0,
NIL, NULL, NULL));
if (trigrec->tgnargs > 0)
{
@ -4324,6 +4325,15 @@ get_rule_expr(Node *node, deparse_context *context,
get_func_expr((FuncExpr *) node, context, showimplicit);
break;
case T_NamedArgExpr:
{
NamedArgExpr *na = (NamedArgExpr *) node;
get_rule_expr((Node *) na->arg, context, showimplicit);
appendStringInfo(buf, " AS %s", quote_identifier(na->name));
}
break;
case T_OpExpr:
get_oper_expr((OpExpr *) node, context);
break;
@ -5187,6 +5197,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
Oid funcoid = expr->funcid;
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
List *argnames;
bool is_variadic;
ListCell *l;
@ -5231,14 +5242,20 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg("too many arguments")));
nargs = 0;
argnames = NIL;
foreach(l, expr->args)
{
argtypes[nargs] = exprType((Node *) lfirst(l));
Node *arg = (Node *) lfirst(l);
if (IsA(arg, NamedArgExpr))
argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
argtypes[nargs] = exprType(arg);
nargs++;
}
appendStringInfo(buf, "%s(",
generate_function_name(funcoid, nargs, argtypes,
generate_function_name(funcoid, nargs,
argnames, argtypes,
&is_variadic));
nargs = 0;
foreach(l, expr->args)
@ -5270,13 +5287,16 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
nargs = 0;
foreach(l, aggref->args)
{
argtypes[nargs] = exprType((Node *) lfirst(l));
Node *arg = (Node *) lfirst(l);
Assert(!IsA(arg, NamedArgExpr));
argtypes[nargs] = exprType(arg);
nargs++;
}
appendStringInfo(buf, "%s(%s",
generate_function_name(aggref->aggfnoid,
nargs, argtypes, NULL),
generate_function_name(aggref->aggfnoid, nargs,
NIL, argtypes, NULL),
aggref->aggdistinct ? "DISTINCT " : "");
/* aggstar can be set only in zero-argument aggregates */
if (aggref->aggstar)
@ -5304,13 +5324,16 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
nargs = 0;
foreach(l, wfunc->args)
{
argtypes[nargs] = exprType((Node *) lfirst(l));
Node *arg = (Node *) lfirst(l);
Assert(!IsA(arg, NamedArgExpr));
argtypes[nargs] = exprType(arg);
nargs++;
}
appendStringInfo(buf, "%s(%s",
generate_function_name(wfunc->winfnoid,
nargs, argtypes, NULL), "");
appendStringInfo(buf, "%s(",
generate_function_name(wfunc->winfnoid, nargs,
NIL, argtypes, NULL));
/* winstar can be set only in zero-argument aggregates */
if (wfunc->winstar)
appendStringInfoChar(buf, '*');
@ -6338,15 +6361,15 @@ generate_relation_name(Oid relid, List *namespaces)
/*
* generate_function_name
* Compute the name to display for a function specified by OID,
* given that it is being called with the specified actual arg types.
* (Arg types matter because of ambiguous-function resolution rules.)
* given that it is being called with the specified actual arg names and
* types. (Those matter because of ambiguous-function resolution rules.)
*
* 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,
bool *is_variadic)
generate_function_name(Oid funcid, int nargs, List *argnames,
Oid *argtypes, bool *is_variadic)
{
HeapTuple proctup;
Form_pg_proc procform;
@ -6371,10 +6394,12 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes,
/*
* The idea here is to schema-qualify only if the parser would fail to
* resolve the correct function given the unqualified func name with the
* specified argtypes.
* specified argtypes. If the function is variadic, we should presume
* that VARIADIC will be included in the call.
*/
p_result = func_get_detail(list_make1(makeString(proname)),
NIL, nargs, argtypes, false, true,
NIL, argnames, nargs, argtypes,
!OidIsValid(procform->provariadic), true,
&p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_true_typeids, NULL);
if ((p_result == FUNCDETAIL_NORMAL ||

View File

@ -7,7 +7,7 @@
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.45 2009/06/11 14:49:05 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.46 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -766,6 +766,92 @@ get_func_arg_info(HeapTuple procTup,
}
/*
* get_func_input_arg_names
*
* Extract the names of input arguments only, given a function's
* proargnames and proargmodes entries in Datum form.
*
* Returns the number of input arguments, which is the length of the
* palloc'd array returned to *arg_names. Entries for unnamed args
* are set to NULL. You don't get anything if proargnames is NULL.
*/
int
get_func_input_arg_names(Datum proargnames, Datum proargmodes,
char ***arg_names)
{
ArrayType *arr;
int numargs;
Datum *argnames;
char *argmodes;
char **inargnames;
int numinargs;
int i;
/* Do nothing if null proargnames */
if (proargnames == PointerGetDatum(NULL))
{
*arg_names = NULL;
return 0;
}
/*
* We expect the arrays to be 1-D arrays of the right types; verify that.
* For proargmodes, we don't need to use deconstruct_array()
* since the array data is just going to look like a C array of values.
*/
arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
if (ARR_NDIM(arr) != 1 ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != TEXTOID)
elog(ERROR, "proargnames is not a 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i',
&argnames, NULL, &numargs);
if (proargmodes != PointerGetDatum(NULL))
{
arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
if (ARR_NDIM(arr) != 1 ||
ARR_DIMS(arr)[0] != numargs ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != CHAROID)
elog(ERROR, "proargmodes is not a 1-D char array");
argmodes = (char *) ARR_DATA_PTR(arr);
}
else
argmodes = NULL;
/* zero elements probably shouldn't happen, but handle it gracefully */
if (numargs <= 0)
{
*arg_names = NULL;
return 0;
}
/* extract input-argument names */
inargnames = (char **) palloc(numargs * sizeof(char *));
numinargs = 0;
for (i = 0; i < numargs; i++)
{
if (argmodes == NULL ||
argmodes[i] == PROARGMODE_IN ||
argmodes[i] == PROARGMODE_INOUT ||
argmodes[i] == PROARGMODE_VARIADIC)
{
char *pname = TextDatumGetCString(argnames[i]);
if (pname[0] != '\0')
inargnames[numinargs] = pname;
else
inargnames[numinargs] = NULL;
numinargs++;
}
}
*arg_names = inargnames;
return numinargs;
}
/*
* get_func_result_name
*

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.542 2009/10/07 22:14:24 alvherre Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.543 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200910071
#define CATALOG_VERSION_NO 200910072
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.59 2009/06/11 14:49:09 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.60 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -32,6 +32,7 @@ typedef struct _FuncCandidateList
int nargs; /* number of arg types returned */
int nvargs; /* number of args to become variadic array */
int ndargs; /* number of defaulted args */
int *argnumbers; /* args' positional indexes, if named call */
Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */
} *FuncCandidateList; /* VARIABLE LENGTH STRUCT */
@ -54,7 +55,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, List *argnames,
bool expand_variadic,
bool expand_defaults);
extern bool FunctionIsVisible(Oid funcid);

View File

@ -9,7 +9,7 @@
*
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/include/funcapi.h,v 1.29 2009/06/11 14:49:08 momjian Exp $
* $PostgreSQL: pgsql/src/include/funcapi.h,v 1.30 2009/10/08 02:39:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -173,6 +173,9 @@ extern int get_func_arg_info(HeapTuple procTup,
Oid **p_argtypes, char ***p_argnames,
char **p_argmodes);
extern int get_func_input_arg_names(Datum proargnames, Datum proargmodes,
char ***arg_names);
extern char *get_func_result_name(Oid functionId);
extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.227 2009/10/05 19:24:48 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.228 2009/10/08 02:39:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -123,6 +123,7 @@ typedef enum NodeTag
T_WindowFunc,
T_ArrayRef,
T_FuncExpr,
T_NamedArgExpr,
T_OpExpr,
T_DistinctExpr,
T_ScalarArrayOpExpr,

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.150 2009/07/16 06:33:45 petere Exp $
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.151 2009/10/08 02:39:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -313,6 +313,29 @@ typedef struct FuncExpr
int location; /* token location, or -1 if unknown */
} FuncExpr;
/*
* NamedArgExpr - a named argument of a function
*
* This node type can only appear in the args list of a FuncCall or FuncExpr
* node. We support pure positional call notation (no named arguments),
* named notation (all arguments are named), and mixed notation (unnamed
* arguments followed by named ones).
*
* Parse analysis sets argnumber to the positional index of the argument,
* but doesn't rearrange the argument list.
*
* The planner will convert argument lists to pure positional notation
* during expression preprocessing, so execution never sees a NamedArgExpr.
*/
typedef struct NamedArgExpr
{
Expr xpr;
Expr *arg; /* the argument expression */
char *name; /* the name */
int argnumber; /* argument's number in positional notation */
int location; /* argument name location, or -1 if unknown */
} NamedArgExpr;
/*
* OpExpr - expression node for an operator invocation
*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.65 2009/05/12 00:56:05 tgl Exp $
* $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.66 2009/10/08 02:39:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -47,7 +47,8 @@ extern Node *ParseFuncOrColumn(ParseState *pstate,
bool agg_star, bool agg_distinct, bool func_variadic,
WindowDef *over, bool is_column, int location);
extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
extern FuncDetailCode func_get_detail(List *funcname,
List *fargs, List *fargnames,
int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype,
@ -68,10 +69,10 @@ extern void make_fn_arguments(ParseState *pstate,
Oid *actual_arg_types,
Oid *declared_arg_types);
extern const char *funcname_signature_string(const char *funcname,
int nargs, const Oid *argtypes);
extern const char *func_signature_string(List *funcname,
int nargs, const Oid *argtypes);
extern const char *funcname_signature_string(const char *funcname, int nargs,
List *argnames, const Oid *argtypes);
extern const char *func_signature_string(List *funcname, int nargs,
List *argnames, const Oid *argtypes);
extern Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
bool noError);

View File

@ -1,5 +1,6 @@
-- Currently this tests polymorphic aggregates and indirectly does some
-- testing of polymorphic SQL functions. It ought to be extended.
-- Tests for other features related to function-calling have snuck in, too.
-- Legend:
-----------
-- A = type is ANY
@ -19,7 +20,7 @@
-- !> = not allowed
-- E = exists
-- NE = not-exists
--
--
-- Possible states:
-- ----------------
-- B = (A || P || N)
@ -60,7 +61,7 @@ CREATE FUNCTION ffp(anyarray) RETURNS anyarray AS
CREATE FUNCTION ffnp(int[]) returns int[] as
'select $1' LANGUAGE SQL;
-- Try to cover all the possible states:
--
--
-- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn
-- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp,
-- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
@ -837,7 +838,7 @@ select dfunc();
-- verify it lists properly
\df dfunc
List of functions
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+-------+------------------+-----------------------------------------------------------+--------
public | dfunc | integer | a integer DEFAULT 1, OUT sum integer, b integer DEFAULT 2 | normal
@ -1005,7 +1006,7 @@ $$ select array_upper($1, 1) $$ language sql;
ERROR: cannot remove parameter defaults from existing function
HINT: Use DROP FUNCTION first.
\df dfunc
List of functions
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+-------+------------------+-------------------------------------------------+--------
public | dfunc | integer | VARIADIC a integer[] DEFAULT ARRAY[]::integer[] | normal
@ -1038,3 +1039,328 @@ select dfunc('Hi');
drop function dfunc(int, int, int);
drop function dfunc(int, int);
drop function dfunc(text);
--
-- Tests for named- and mixed-notation function calling
--
create function dfunc(a int, b int, c int = 0, d int = 0)
returns table (a int, b int, c int, d int) as $$
select $1, $2, $3, $4;
$$ language sql;
select (dfunc(10,20,30)).*;
a | b | c | d
----+----+----+---
10 | 20 | 30 | 0
(1 row)
select (dfunc(10 as a, 20 as b, 30 as c)).*;
a | b | c | d
----+----+----+---
10 | 20 | 30 | 0
(1 row)
select * from dfunc(10 as a, 20 as b);
a | b | c | d
----+----+---+---
10 | 20 | 0 | 0
(1 row)
select * from dfunc(10 as b, 20 as a);
a | b | c | d
----+----+---+---
20 | 10 | 0 | 0
(1 row)
select * from dfunc(0); -- fail
ERROR: function dfunc(integer) does not exist
LINE 1: select * from dfunc(0);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select * from dfunc(1,2);
a | b | c | d
---+---+---+---
1 | 2 | 0 | 0
(1 row)
select * from dfunc(1,2,3 as c);
a | b | c | d
---+---+---+---
1 | 2 | 3 | 0
(1 row)
select * from dfunc(1,2,3 as d);
a | b | c | d
---+---+---+---
1 | 2 | 0 | 3
(1 row)
select * from dfunc(10 as x, 20 as b, 30 as x); -- fail, duplicate name
ERROR: argument name "x" used more than once
LINE 1: select * from dfunc(10 as x, 20 as b, 30 as x);
^
select * from dfunc(10, 20 as b, 30); -- fail, named args must be last
ERROR: positional argument cannot follow named argument
LINE 1: select * from dfunc(10, 20 as b, 30);
^
select * from dfunc(10 as x, 20 as b, 30 as c); -- fail, unknown param
ERROR: function dfunc(integer AS x, integer AS b, integer AS c) does not exist
LINE 1: select * from dfunc(10 as x, 20 as b, 30 as c);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select * from dfunc(10, 10, 20 as a); -- fail, a overlaps positional parameter
ERROR: function dfunc(integer, integer, integer AS a) does not exist
LINE 1: select * from dfunc(10, 10, 20 as a);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select * from dfunc(1,2 as c,3 as d); -- fail, no value for b
ERROR: function dfunc(integer, integer AS c, integer AS d) does not exist
LINE 1: select * from dfunc(1,2 as c,3 as d);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function dfunc(int, int, int, int);
-- test with different parameter types
create function dfunc(a varchar, b numeric, c date = current_date)
returns table (a varchar, b numeric, c date) as $$
select $1, $2, $3;
$$ language sql;
select (dfunc('Hello World', 20, '2009-07-25'::date)).*;
a | b | c
-------------+----+------------
Hello World | 20 | 07-25-2009
(1 row)
select * from dfunc('Hello World', 20, '2009-07-25'::date);
a | b | c
-------------+----+------------
Hello World | 20 | 07-25-2009
(1 row)
select * from dfunc('2009-07-25'::date as c, 'Hello World' as a, 20 as b);
a | b | c
-------------+----+------------
Hello World | 20 | 07-25-2009
(1 row)
select * from dfunc('Hello World', 20 as b, '2009-07-25'::date as c);
a | b | c
-------------+----+------------
Hello World | 20 | 07-25-2009
(1 row)
select * from dfunc('Hello World', '2009-07-25'::date as c, 20 as b);
a | b | c
-------------+----+------------
Hello World | 20 | 07-25-2009
(1 row)
select * from dfunc('Hello World', 20 as c, '2009-07-25'::date as b); -- fail
ERROR: function dfunc(unknown, integer AS c, date AS b) does not exist
LINE 1: select * from dfunc('Hello World', 20 as c, '2009-07-25'::da...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function dfunc(varchar, numeric, date);
-- test out parameters with named params
create function dfunc(a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric)
returns record as $$
select $1, $2;
$$ language sql;
select (dfunc()).*;
_a | _c
-------+----
def a |
(1 row)
select * from dfunc();
_a | _c
-------+----
def a |
(1 row)
select * from dfunc('Hello', 100);
_a | _c
-------+-----
Hello | 100
(1 row)
select * from dfunc('Hello' as a, 100 as c);
_a | _c
-------+-----
Hello | 100
(1 row)
select * from dfunc(100 as c, 'Hello' as a);
_a | _c
-------+-----
Hello | 100
(1 row)
select * from dfunc('Hello');
_a | _c
-------+----
Hello |
(1 row)
select * from dfunc('Hello', 100 as c);
_a | _c
-------+-----
Hello | 100
(1 row)
select * from dfunc(100 as c);
_a | _c
-------+-----
def a | 100
(1 row)
-- fail, can no longer change an input parameter's name
create or replace function dfunc(a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric)
returns record as $$
select $1, $2;
$$ language sql;
ERROR: cannot change name of input parameter "c"
HINT: Use DROP FUNCTION first.
create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric)
returns record as $$
select $1, $2;
$$ language sql;
ERROR: cannot change name of input parameter "c"
HINT: Use DROP FUNCTION first.
drop function dfunc(varchar, numeric);
--fail, named parameters are not unique
create function testfoo(a int, a int) returns int as $$ select 1;$$ language sql;
ERROR: parameter name "a" used more than once
create function testfoo(int, out a int, out a int) returns int as $$ select 1;$$ language sql;
ERROR: parameter name "a" used more than once
create function testfoo(out a int, inout a int) returns int as $$ select 1;$$ language sql;
ERROR: parameter name "a" used more than once
create function testfoo(a int, inout a int) returns int as $$ select 1;$$ language sql;
ERROR: parameter name "a" used more than once
-- valid
create function testfoo(a int, out a int) returns int as $$ select $1;$$ language sql;
select testfoo(37);
testfoo
---------
37
(1 row)
drop function testfoo(int);
create function testfoo(a int) returns table(a int) as $$ select $1;$$ language sql;
select * from testfoo(37);
a
----
37
(1 row)
drop function testfoo(int);
-- test polymorphic params and defaults
create function dfunc(a anyelement, b anyelement = null, flag bool = true)
returns anyelement as $$
select case when $3 then $1 else $2 end;
$$ language sql;
select dfunc(1,2);
dfunc
-------
1
(1 row)
select dfunc('a'::text, 'b'); -- positional notation with default
dfunc
-------
a
(1 row)
select dfunc(1 as a, 2 as b);
dfunc
-------
1
(1 row)
select dfunc('a'::text as a, 'b' as b);
dfunc
-------
a
(1 row)
select dfunc('a'::text as a, 'b' as b, false as flag); -- named notation
dfunc
-------
b
(1 row)
select dfunc('b'::text as b, 'a' as a); -- named notation with default
dfunc
-------
a
(1 row)
select dfunc('a'::text as a, true as flag); -- named notation with default
dfunc
-------
a
(1 row)
select dfunc('a'::text as a, false as flag); -- named notation with default
dfunc
-------
(1 row)
select dfunc('b'::text as b, 'a' as a, true as flag); -- named notation
dfunc
-------
a
(1 row)
select dfunc('a'::text, 'b', false); -- full positional notation
dfunc
-------
b
(1 row)
select dfunc('a'::text, 'b', false as flag); -- mixed notation
dfunc
-------
b
(1 row)
select dfunc('a'::text, 'b', true); -- full positional notation
dfunc
-------
a
(1 row)
select dfunc('a'::text, 'b', true as flag); -- mixed notation
dfunc
-------
a
(1 row)
-- check reverse-listing of named-arg calls
CREATE VIEW dfview AS
SELECT q1, q2,
dfunc(q1,q2, q1>q2 as flag) as c3,
dfunc(q1, q1<q2 as flag, q2 AS b) as c4
FROM int8_tbl;
select * from dfview;
q1 | q2 | c3 | c4
------------------+-------------------+------------------+-------------------
123 | 456 | 456 | 123
123 | 4567890123456789 | 4567890123456789 | 123
4567890123456789 | 123 | 4567890123456789 | 123
4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789
(5 rows)
\d dfview
View "public.dfview"
Column | Type | Modifiers
--------+--------+-----------
q1 | bigint |
q2 | bigint |
c3 | bigint |
c4 | bigint |
View definition:
SELECT int8_tbl.q1, int8_tbl.q2, dfunc(int8_tbl.q1, int8_tbl.q2, int8_tbl.q1 > int8_tbl.q2 AS flag) AS c3, dfunc(int8_tbl.q1, int8_tbl.q1 < int8_tbl.q2 AS flag, int8_tbl.q2 AS b) AS c4
FROM int8_tbl;
drop view dfview;
drop function dfunc(anyelement, anyelement, bool);

View File

@ -159,7 +159,7 @@ SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
1 | 1 | Joe
(1 row)
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
(fooid int, foosubid int, fooname text);
SELECT * FROM vw_getfoo;
fooid | foosubid | fooname
@ -515,7 +515,13 @@ SELECT * FROM dup('xyz'::text);
xyz | {xyz,xyz}
(1 row)
-- equivalent specification
-- fails, as we are attempting to rename first argument
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot change name of input parameter "f1"
HINT: Use DROP FUNCTION first.
DROP FUNCTION dup(anyelement);
-- equivalent behavior, though different name exposed for input arg
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
SELECT dup(22);

View File

@ -1,5 +1,6 @@
-- Currently this tests polymorphic aggregates and indirectly does some
-- testing of polymorphic SQL functions. It ought to be extended.
-- Tests for other features related to function-calling have snuck in, too.
-- Legend:
@ -21,7 +22,7 @@
-- !> = not allowed
-- E = exists
-- NE = not-exists
--
--
-- Possible states:
-- ----------------
-- B = (A || P || N)
@ -69,7 +70,7 @@ CREATE FUNCTION ffnp(int[]) returns int[] as
'select $1' LANGUAGE SQL;
-- Try to cover all the possible states:
--
--
-- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn
-- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp,
-- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
@ -624,3 +625,123 @@ select dfunc('Hi');
drop function dfunc(int, int, int);
drop function dfunc(int, int);
drop function dfunc(text);
--
-- Tests for named- and mixed-notation function calling
--
create function dfunc(a int, b int, c int = 0, d int = 0)
returns table (a int, b int, c int, d int) as $$
select $1, $2, $3, $4;
$$ language sql;
select (dfunc(10,20,30)).*;
select (dfunc(10 as a, 20 as b, 30 as c)).*;
select * from dfunc(10 as a, 20 as b);
select * from dfunc(10 as b, 20 as a);
select * from dfunc(0); -- fail
select * from dfunc(1,2);
select * from dfunc(1,2,3 as c);
select * from dfunc(1,2,3 as d);
select * from dfunc(10 as x, 20 as b, 30 as x); -- fail, duplicate name
select * from dfunc(10, 20 as b, 30); -- fail, named args must be last
select * from dfunc(10 as x, 20 as b, 30 as c); -- fail, unknown param
select * from dfunc(10, 10, 20 as a); -- fail, a overlaps positional parameter
select * from dfunc(1,2 as c,3 as d); -- fail, no value for b
drop function dfunc(int, int, int, int);
-- test with different parameter types
create function dfunc(a varchar, b numeric, c date = current_date)
returns table (a varchar, b numeric, c date) as $$
select $1, $2, $3;
$$ language sql;
select (dfunc('Hello World', 20, '2009-07-25'::date)).*;
select * from dfunc('Hello World', 20, '2009-07-25'::date);
select * from dfunc('2009-07-25'::date as c, 'Hello World' as a, 20 as b);
select * from dfunc('Hello World', 20 as b, '2009-07-25'::date as c);
select * from dfunc('Hello World', '2009-07-25'::date as c, 20 as b);
select * from dfunc('Hello World', 20 as c, '2009-07-25'::date as b); -- fail
drop function dfunc(varchar, numeric, date);
-- test out parameters with named params
create function dfunc(a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric)
returns record as $$
select $1, $2;
$$ language sql;
select (dfunc()).*;
select * from dfunc();
select * from dfunc('Hello', 100);
select * from dfunc('Hello' as a, 100 as c);
select * from dfunc(100 as c, 'Hello' as a);
select * from dfunc('Hello');
select * from dfunc('Hello', 100 as c);
select * from dfunc(100 as c);
-- fail, can no longer change an input parameter's name
create or replace function dfunc(a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric)
returns record as $$
select $1, $2;
$$ language sql;
create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric)
returns record as $$
select $1, $2;
$$ language sql;
drop function dfunc(varchar, numeric);
--fail, named parameters are not unique
create function testfoo(a int, a int) returns int as $$ select 1;$$ language sql;
create function testfoo(int, out a int, out a int) returns int as $$ select 1;$$ language sql;
create function testfoo(out a int, inout a int) returns int as $$ select 1;$$ language sql;
create function testfoo(a int, inout a int) returns int as $$ select 1;$$ language sql;
-- valid
create function testfoo(a int, out a int) returns int as $$ select $1;$$ language sql;
select testfoo(37);
drop function testfoo(int);
create function testfoo(a int) returns table(a int) as $$ select $1;$$ language sql;
select * from testfoo(37);
drop function testfoo(int);
-- test polymorphic params and defaults
create function dfunc(a anyelement, b anyelement = null, flag bool = true)
returns anyelement as $$
select case when $3 then $1 else $2 end;
$$ language sql;
select dfunc(1,2);
select dfunc('a'::text, 'b'); -- positional notation with default
select dfunc(1 as a, 2 as b);
select dfunc('a'::text as a, 'b' as b);
select dfunc('a'::text as a, 'b' as b, false as flag); -- named notation
select dfunc('b'::text as b, 'a' as a); -- named notation with default
select dfunc('a'::text as a, true as flag); -- named notation with default
select dfunc('a'::text as a, false as flag); -- named notation with default
select dfunc('b'::text as b, 'a' as a, true as flag); -- named notation
select dfunc('a'::text, 'b', false); -- full positional notation
select dfunc('a'::text, 'b', false as flag); -- mixed notation
select dfunc('a'::text, 'b', true); -- full positional notation
select dfunc('a'::text, 'b', true as flag); -- mixed notation
-- check reverse-listing of named-arg calls
CREATE VIEW dfview AS
SELECT q1, q2,
dfunc(q1,q2, q1>q2 as flag) as c3,
dfunc(q1, q1<q2 as flag, q2 AS b) as c4
FROM int8_tbl;
select * from dfview;
\d dfview
drop view dfview;
drop function dfunc(anyelement, anyelement, bool);

View File

@ -70,7 +70,7 @@ DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
(fooid int, foosubid int, fooname text);
SELECT * FROM vw_getfoo;
@ -251,7 +251,13 @@ SELECT dup('xyz'); -- fails
SELECT dup('xyz'::text);
SELECT * FROM dup('xyz'::text);
-- equivalent specification
-- fails, as we are attempting to rename first argument
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
DROP FUNCTION dup(anyelement);
-- equivalent behavior, though different name exposed for input arg
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
SELECT dup(22);