Commit 717fa274 authored by Tom Lane's avatar Tom Lane

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
parent 2eda8dfb
<!-- <!--
$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"> <refentry id="SQL-CREATEFUNCTION">
...@@ -65,7 +65,7 @@ CREATE [ OR REPLACE ] FUNCTION ...@@ -65,7 +65,7 @@ CREATE [ OR REPLACE ] FUNCTION
Also, <command>CREATE OR REPLACE FUNCTION</command> will not let Also, <command>CREATE OR REPLACE FUNCTION</command> will not let
you change the return type of an existing function. To do that, you change the return type of an existing function. To do that,
you must drop and recreate the function. (When using <literal>OUT</> 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.) <literal>OUT</> parameters except by dropping the function.)
</para> </para>
...@@ -121,8 +121,11 @@ CREATE [ OR REPLACE ] FUNCTION ...@@ -121,8 +121,11 @@ CREATE [ OR REPLACE ] FUNCTION
<para> <para>
The name of an argument. Some languages (currently only PL/pgSQL) let The name of an argument. Some languages (currently only PL/pgSQL) let
you use the name in the function body. For other languages the you use the name in the function body. For other languages the
name of an input argument is just extra documentation. But the name name of an input argument is just extra documentation, so far as
of an output argument is significant, since it defines the column 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 name in the result row type. (If you omit the name for an output
argument, the system will choose a default column name.) argument, the system will choose a default column name.)
</para> </para>
...@@ -570,6 +573,18 @@ CREATE FUNCTION foo(int, int default 42) ... ...@@ -570,6 +573,18 @@ CREATE FUNCTION foo(int, int default 42) ...
to replace it (this includes being a member of the owning role). to replace it (this includes being a member of the owning role).
</para> </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>
<refsect1 id="sql-createfunction-examples"> <refsect1 id="sql-createfunction-examples">
......
<!-- $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"> <chapter id="source">
<title>PostgreSQL Coding Conventions</title> <title>PostgreSQL Coding Conventions</title>
...@@ -125,7 +125,7 @@ ereport(ERROR, ...@@ -125,7 +125,7 @@ ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION), (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function %s is not unique", errmsg("function %s is not unique",
func_signature_string(funcname, nargs, func_signature_string(funcname, nargs,
actual_arg_types)), NIL, actual_arg_types)),
errhint("Unable to choose a best candidate function. " errhint("Unable to choose a best candidate function. "
"You might need to add explicit typecasts."))); "You might need to add explicit typecasts.")));
</programlisting> </programlisting>
......
<!-- $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"> <chapter id="sql-syntax">
<title>SQL Syntax</title> <title>SQL Syntax</title>
...@@ -1505,6 +1505,11 @@ sqrt(2) ...@@ -1505,6 +1505,11 @@ sqrt(2)
The list of built-in functions is in <xref linkend="functions">. The list of built-in functions is in <xref linkend="functions">.
Other functions can be added by the user. Other functions can be added by the user.
</para> </para>
<para>
The arguments can optionally have names attached.
See <xref linkend="sql-syntax-calling-funcs"> for details.
</para>
</sect2> </sect2>
<sect2 id="syntax-aggregates"> <sect2 id="syntax-aggregates">
...@@ -2123,4 +2128,168 @@ SELECT ... WHERE CASE WHEN x &gt; 0 THEN y/x &gt; 1.5 ELSE false END; ...@@ -2123,4 +2128,168 @@ SELECT ... WHERE CASE WHEN x &gt; 0 THEN y/x &gt; 1.5 ELSE false END;
</sect2> </sect2>
</sect1> </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> </chapter>
<!-- $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"> <sect1 id="xfunc">
<title>User-Defined Functions</title> <title>User-Defined Functions</title>
...@@ -517,6 +517,39 @@ SELECT getname(new_emp()); ...@@ -517,6 +517,39 @@ SELECT getname(new_emp());
</para> </para>
</sect2> </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"> <sect2 id="xfunc-output-parameters">
<title><acronym>SQL</> Functions with Output Parameters</title> <title><acronym>SQL</> Functions with Output Parameters</title>
...@@ -571,7 +604,10 @@ LANGUAGE SQL; ...@@ -571,7 +604,10 @@ LANGUAGE SQL;
</screen> </screen>
but not having to bother with the separate composite type definition 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>
<para> <para>
...@@ -621,7 +657,7 @@ DROP FUNCTION sum_n_product (int, int); ...@@ -621,7 +657,7 @@ DROP FUNCTION sum_n_product (int, int);
must be declared as being of an array type. For example: must be declared as being of an array type. For example:
<screen> <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); SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL; $$ LANGUAGE SQL;
...@@ -661,6 +697,25 @@ SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]); ...@@ -661,6 +697,25 @@ SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
normally. <literal>VARIADIC</> can only be attached to the last normally. <literal>VARIADIC</> can only be attached to the last
actual argument of a function call. actual argument of a function call.
</para> </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>
<sect2 id="xfunc-sql-parameter-defaults"> <sect2 id="xfunc-sql-parameter-defaults">
...@@ -677,7 +732,9 @@ SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]); ...@@ -677,7 +732,9 @@ SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
called with insufficiently many actual arguments. Since arguments called with insufficiently many actual arguments. Since arguments
can only be omitted from the end of the actual argument list, all can only be omitted from the end of the actual argument list, all
parameters after a parameter with a default value have to have 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>
<para> <para>
...@@ -712,7 +769,7 @@ SELECT foo(); -- fails since there is no default for the first argument ...@@ -712,7 +769,7 @@ SELECT foo(); -- fails since there is no default for the first argument
ERROR: function foo() does not exist ERROR: function foo() does not exist
</screen> </screen>
The <literal>=</literal> sign can also be used in place of the The <literal>=</literal> sign can also be used in place of the
key word <literal>DEFAULT</literal>, key word <literal>DEFAULT</literal>.
</para> </para>
</sect2> </sect2>
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -321,7 +321,8 @@ lookup_agg_function(List *fnName,
* function's return value. it also returns the true argument types to * function's return value. it also returns the true argument types to
* the function. * 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, &fnOid, rettype, &retset, &nvargs,
&true_oid_array, NULL); &true_oid_array, NULL);
...@@ -330,12 +331,14 @@ lookup_agg_function(List *fnName, ...@@ -330,12 +331,14 @@ lookup_agg_function(List *fnName,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", errmsg("function %s does not exist",
func_signature_string(fnName, nargs, input_types)))); func_signature_string(fnName, nargs,
NIL, input_types))));
if (retset) if (retset)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s returns a set", 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 * If there are any polymorphic types involved, enforce consistency, and
...@@ -359,7 +362,8 @@ lookup_agg_function(List *fnName, ...@@ -359,7 +362,8 @@ lookup_agg_function(List *fnName,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s requires run-time type coercion", 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 */ /* Check aggregate creator has permission to call the function */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -348,6 +348,8 @@ ProcedureCreate(const char *procedureName,
{ {
/* There is one; okay to replace it? */ /* There is one; okay to replace it? */
Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
Datum proargnames;
bool isnull;
if (!replace) if (!replace)
ereport(ERROR, ereport(ERROR,
...@@ -393,6 +395,49 @@ ProcedureCreate(const char *procedureName, ...@@ -393,6 +395,49 @@ ProcedureCreate(const char *procedureName,
errhint("Use DROP FUNCTION first."))); 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 * If there are existing defaults, check compatibility: redefinition
* must not remove any defaults nor change their types. (Removing a * must not remove any defaults nor change their types. (Removing a
...@@ -404,7 +449,6 @@ ProcedureCreate(const char *procedureName, ...@@ -404,7 +449,6 @@ ProcedureCreate(const char *procedureName,
if (oldproc->pronargdefaults != 0) if (oldproc->pronargdefaults != 0)
{ {
Datum proargdefaults; Datum proargdefaults;
bool isnull;
List *oldDefaults; List *oldDefaults;
ListCell *oldlc; ListCell *oldlc;
ListCell *newlc; ListCell *newlc;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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 * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -297,6 +297,7 @@ RenameAggregate(List *name, List *args, const char *newname) ...@@ -297,6 +297,7 @@ RenameAggregate(List *name, List *args, const char *newname)
errmsg("function %s already exists in schema \"%s\"", errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname, funcname_signature_string(newname,
procForm->pronargs, procForm->pronargs,
NIL,
procForm->proargtypes.values), procForm->proargtypes.values),
get_namespace_name(namespaceOid)))); get_namespace_name(namespaceOid))));
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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 * DESCRIPTION
* These routines take the parse tree and pick out the * These routines take the parse tree and pick out the
...@@ -285,6 +285,39 @@ examine_parameter_list(List *parameters, Oid languageOid, ...@@ -285,6 +285,39 @@ examine_parameter_list(List *parameters, Oid languageOid,
if (fp->name && fp->name[0]) 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); paramNames[i] = CStringGetTextDatum(fp->name);
have_names = true; have_names = true;
} }
...@@ -1097,6 +1130,7 @@ RenameFunction(List *name, List *argtypes, const char *newname) ...@@ -1097,6 +1130,7 @@ RenameFunction(List *name, List *argtypes, const char *newname)
errmsg("function %s already exists in schema \"%s\"", errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname, funcname_signature_string(newname,
procForm->pronargs, procForm->pronargs,
NIL,
procForm->proargtypes.values), procForm->proargtypes.values),
get_namespace_name(namespaceOid)))); get_namespace_name(namespaceOid))));
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -107,7 +107,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("function %s should return type %s", errmsg("function %s should return type %s",
func_signature_string(funcName, nargs, typeId), func_signature_string(funcName, nargs, NIL, typeId),
format_type_be(retTypeId)))); format_type_be(retTypeId))));
return ObjectIdGetDatum(procOid); return ObjectIdGetDatum(procOid);
...@@ -945,7 +945,7 @@ get_ts_template_func(DefElem *defel, int attnum) ...@@ -945,7 +945,7 @@ get_ts_template_func(DefElem *defel, int attnum)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("function %s should return type %s", errmsg("function %s should return type %s",
func_signature_string(funcName, nargs, typeId), func_signature_string(funcName, nargs, NIL, typeId),
format_type_be(retTypeId)))); format_type_be(retTypeId))));
return ObjectIdGetDatum(procOid); return ObjectIdGetDatum(procOid);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -1267,7 +1267,7 @@ findTypeInputFunction(List *procname, Oid typeOid) ...@@ -1267,7 +1267,7 @@ findTypeInputFunction(List *procname, Oid typeOid)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", errmsg("function %s does not exist",
func_signature_string(procname, 1, argList)))); func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */ return InvalidOid; /* keep compiler quiet */
} }
...@@ -1318,7 +1318,7 @@ findTypeOutputFunction(List *procname, Oid typeOid) ...@@ -1318,7 +1318,7 @@ findTypeOutputFunction(List *procname, Oid typeOid)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", errmsg("function %s does not exist",
func_signature_string(procname, 1, argList)))); func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */ return InvalidOid; /* keep compiler quiet */
} }
...@@ -1349,7 +1349,7 @@ findTypeReceiveFunction(List *procname, Oid typeOid) ...@@ -1349,7 +1349,7 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", errmsg("function %s does not exist",
func_signature_string(procname, 1, argList)))); func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */ return InvalidOid; /* keep compiler quiet */
} }
...@@ -1372,7 +1372,7 @@ findTypeSendFunction(List *procname, Oid typeOid) ...@@ -1372,7 +1372,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", errmsg("function %s does not exist",
func_signature_string(procname, 1, argList)))); func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */ return InvalidOid; /* keep compiler quiet */
} }
...@@ -1393,7 +1393,7 @@ findTypeTypmodinFunction(List *procname) ...@@ -1393,7 +1393,7 @@ findTypeTypmodinFunction(List *procname)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", 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) if (get_func_rettype(procOid) != INT4OID)
ereport(ERROR, ereport(ERROR,
...@@ -1420,7 +1420,7 @@ findTypeTypmodoutFunction(List *procname) ...@@ -1420,7 +1420,7 @@ findTypeTypmodoutFunction(List *procname)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", 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) if (get_func_rettype(procOid) != CSTRINGOID)
ereport(ERROR, ereport(ERROR,
...@@ -1447,7 +1447,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid) ...@@ -1447,7 +1447,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", 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) if (get_func_rettype(procOid) != BOOLOID)
ereport(ERROR, ereport(ERROR,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1019,6 +1019,22 @@ _copyFuncExpr(FuncExpr *from)
return newnode; 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 * _copyOpExpr
*/ */
...@@ -3587,6 +3603,9 @@ copyObject(void *from) ...@@ -3587,6 +3603,9 @@ copyObject(void *from)
case T_FuncExpr: case T_FuncExpr:
retval = _copyFuncExpr(from); retval = _copyFuncExpr(from);
break; break;
case T_NamedArgExpr:
retval = _copyNamedArgExpr(from);
break;
case T_OpExpr: case T_OpExpr:
retval = _copyOpExpr(from); retval = _copyOpExpr(from);
break; break;
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -241,6 +241,17 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
return true; 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 static bool
_equalOpExpr(OpExpr *a, OpExpr *b) _equalOpExpr(OpExpr *a, OpExpr *b)
{ {
...@@ -2375,6 +2386,9 @@ equal(void *a, void *b) ...@@ -2375,6 +2386,9 @@ equal(void *a, void *b)
case T_FuncExpr: case T_FuncExpr:
retval = _equalFuncExpr(a, b); retval = _equalFuncExpr(a, b);
break; break;
case T_NamedArgExpr:
retval = _equalNamedArgExpr(a, b);
break;
case T_OpExpr: case T_OpExpr:
retval = _equalOpExpr(a, b); retval = _equalOpExpr(a, b);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -69,6 +69,9 @@ exprType(Node *expr)
case T_FuncExpr: case T_FuncExpr:
type = ((FuncExpr *) expr)->funcresulttype; type = ((FuncExpr *) expr)->funcresulttype;
break; break;
case T_NamedArgExpr:
type = exprType((Node *) ((NamedArgExpr *) expr)->arg);
break;
case T_OpExpr: case T_OpExpr:
type = ((OpExpr *) expr)->opresulttype; type = ((OpExpr *) expr)->opresulttype;
break; break;
...@@ -259,6 +262,8 @@ exprTypmod(Node *expr) ...@@ -259,6 +262,8 @@ exprTypmod(Node *expr)
return coercedTypmod; return coercedTypmod;
} }
break; break;
case T_NamedArgExpr:
return exprTypmod((Node *) ((NamedArgExpr *) expr)->arg);
case T_SubLink: case T_SubLink:
{ {
SubLink *sublink = (SubLink *) expr; SubLink *sublink = (SubLink *) expr;
...@@ -676,6 +681,15 @@ exprLocation(Node *expr) ...@@ -676,6 +681,15 @@ exprLocation(Node *expr)
exprLocation((Node *) fexpr->args)); exprLocation((Node *) fexpr->args));
} }
break; 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_OpExpr:
case T_DistinctExpr: /* struct-equivalent to OpExpr */ case T_DistinctExpr: /* struct-equivalent to OpExpr */
case T_NullIfExpr: /* struct-equivalent to OpExpr */ case T_NullIfExpr: /* struct-equivalent to OpExpr */
...@@ -1117,6 +1131,8 @@ expression_tree_walker(Node *node, ...@@ -1117,6 +1131,8 @@ expression_tree_walker(Node *node,
return true; return true;
} }
break; break;
case T_NamedArgExpr:
return walker(((NamedArgExpr *) node)->arg, context);
case T_OpExpr: case T_OpExpr:
{ {
OpExpr *expr = (OpExpr *) node; OpExpr *expr = (OpExpr *) node;
...@@ -1623,6 +1639,16 @@ expression_tree_mutator(Node *node, ...@@ -1623,6 +1639,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; 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: case T_OpExpr:
{ {
OpExpr *expr = (OpExpr *) node; OpExpr *expr = (OpExpr *) node;
...@@ -2382,6 +2408,8 @@ bool ...@@ -2382,6 +2408,8 @@ bool
/* function name is deemed uninteresting */ /* function name is deemed uninteresting */
} }
break; break;
case T_NamedArgExpr:
return walker(((NamedArgExpr *) node)->arg, context);
case T_A_Indices: case T_A_Indices:
{ {
A_Indices *indices = (A_Indices *) node; A_Indices *indices = (A_Indices *) node;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -875,6 +875,17 @@ _outFuncExpr(StringInfo str, FuncExpr *node) ...@@ -875,6 +875,17 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
WRITE_LOCATION_FIELD(location); 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 static void
_outOpExpr(StringInfo str, OpExpr *node) _outOpExpr(StringInfo str, OpExpr *node)
{ {
...@@ -2514,6 +2525,9 @@ _outNode(StringInfo str, void *obj) ...@@ -2514,6 +2525,9 @@ _outNode(StringInfo str, void *obj)
case T_FuncExpr: case T_FuncExpr:
_outFuncExpr(str, obj); _outFuncExpr(str, obj);
break; break;
case T_NamedArgExpr:
_outNamedArgExpr(str, obj);
break;
case T_OpExpr: case T_OpExpr:
_outOpExpr(str, obj); _outOpExpr(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -525,6 +525,22 @@ _readFuncExpr(void) ...@@ -525,6 +525,22 @@ _readFuncExpr(void)
READ_DONE(); 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 * _readOpExpr
*/ */
...@@ -1207,6 +1223,8 @@ parseNodeString(void) ...@@ -1207,6 +1223,8 @@ parseNodeString(void)
return_value = _readArrayRef(); return_value = _readArrayRef();
else if (MATCH("FUNCEXPR", 8)) else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr(); return_value = _readFuncExpr();
else if (MATCH("NAMEDARGEXPR", 12))
return_value = _readNamedArgExpr();
else if (MATCH("OPEXPR", 6)) else if (MATCH("OPEXPR", 6))
return_value = _readOpExpr(); return_value = _readOpExpr();
else if (MATCH("DISTINCTEXPR", 12)) else if (MATCH("DISTINCTEXPR", 12))
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -526,7 +526,8 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
/* /*
* Simplify constant expressions. * 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 * of any default arguments for functions. To ensure that happens, we
* *must* process all expressions here. Previous PG versions sometimes * *must* process all expressions here. Previous PG versions sometimes
* skipped const-simplification if it didn't seem worth the trouble, but * 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, ...@@ -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 * Currently, we disallow sublinks in standalone expressions, so there's no
* real "planning" involved here. (That might not always be true though.) * 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 * What we must do is run eval_const_expressions to ensure that any function
* default arguments get inserted. The fact that constant subexpressions * calls are converted to positional notation and function default arguments
* get simplified is a side-effect that is useful when the expression will * get inserted. The fact that constant subexpressions get simplified is a
* get evaluated more than once. Also, we must fix operator function IDs. * 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 * 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 * tree. (It would actually be okay to apply fix_opfuncids to it, but since
...@@ -2672,7 +2674,10 @@ expression_planner(Expr *expr) ...@@ -2672,7 +2674,10 @@ expression_planner(Expr *expr)
{ {
Node *result; 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); result = eval_const_expressions(NULL, (Node *) expr);
/* Fill in opfuncid values if missing */ /* Fill in opfuncid values if missing */
......
This diff is collapsed.
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -354,6 +354,8 @@ static TypeName *TableFuncTypeName(List *columns); ...@@ -354,6 +354,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <node> def_arg columnElem where_clause where_or_current_clause %type <node> def_arg columnElem where_clause where_or_current_clause
a_expr b_expr c_expr func_expr AexprConst indirection_el a_expr b_expr c_expr func_expr AexprConst indirection_el
columnref in_expr having_clause func_table array_expr 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 <list> row type_list array_expr_list
%type <node> case_expr case_arg when_clause case_default %type <node> case_expr case_arg when_clause case_default
%type <list> when_clause_list %type <list> when_clause_list
...@@ -9055,7 +9057,7 @@ func_expr: func_name '(' ')' over_clause ...@@ -9055,7 +9057,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1; n->location = @1;
$$ = (Node *)n; $$ = (Node *)n;
} }
| func_name '(' expr_list ')' over_clause | func_name '(' func_arg_list ')' over_clause
{ {
FuncCall *n = makeNode(FuncCall); FuncCall *n = makeNode(FuncCall);
n->funcname = $1; n->funcname = $1;
...@@ -9067,7 +9069,7 @@ func_expr: func_name '(' ')' over_clause ...@@ -9067,7 +9069,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1; n->location = @1;
$$ = (Node *)n; $$ = (Node *)n;
} }
| func_name '(' VARIADIC a_expr ')' over_clause | func_name '(' VARIADIC func_arg_expr ')' over_clause
{ {
FuncCall *n = makeNode(FuncCall); FuncCall *n = makeNode(FuncCall);
n->funcname = $1; n->funcname = $1;
...@@ -9079,7 +9081,7 @@ func_expr: func_name '(' ')' over_clause ...@@ -9079,7 +9081,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1; n->location = @1;
$$ = (Node *)n; $$ = (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); FuncCall *n = makeNode(FuncCall);
n->funcname = $1; n->funcname = $1;
...@@ -9091,7 +9093,7 @@ func_expr: func_name '(' ')' over_clause ...@@ -9091,7 +9093,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1; n->location = @1;
$$ = (Node *)n; $$ = (Node *)n;
} }
| func_name '(' ALL expr_list ')' over_clause | func_name '(' ALL func_arg_list ')' over_clause
{ {
FuncCall *n = makeNode(FuncCall); FuncCall *n = makeNode(FuncCall);
n->funcname = $1; n->funcname = $1;
...@@ -9107,7 +9109,7 @@ func_expr: func_name '(' ')' over_clause ...@@ -9107,7 +9109,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1; n->location = @1;
$$ = (Node *)n; $$ = (Node *)n;
} }
| func_name '(' DISTINCT expr_list ')' over_clause | func_name '(' DISTINCT func_arg_list ')' over_clause
{ {
FuncCall *n = makeNode(FuncCall); FuncCall *n = makeNode(FuncCall);
n->funcname = $1; n->funcname = $1;
...@@ -9830,6 +9832,32 @@ expr_list: a_expr ...@@ -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 { $$ = list_make1($1); }
| type_list ',' Typename { $$ = lappend($1, $3); } | type_list ',' Typename { $$ = lappend($1, $3); }
; ;
...@@ -10296,10 +10324,27 @@ AexprConst: Iconst ...@@ -10296,10 +10324,27 @@ AexprConst: Iconst
t->location = @1; t->location = @1;
$$ = makeStringConstCast($2, @2, t); $$ = makeStringConstCast($2, @2, t);
} }
| func_name '(' expr_list ')' Sconst | func_name '(' func_arg_list ')' Sconst
{ {
/* generic syntax with a type modifier */ /* generic syntax with a type modifier */
TypeName *t = makeTypeNameFromNameList($1); 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->typmods = $3;
t->location = @1; t->location = @1;
$$ = makeStringConstCast($5, @5, t); $$ = makeStringConstCast($5, @5, t);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -228,6 +228,15 @@ transformExpr(ParseState *pstate, Node *expr)
result = transformFuncCall(pstate, (FuncCall *) expr); result = transformFuncCall(pstate, (FuncCall *) expr);
break; break;
case T_NamedArgExpr:
{
NamedArgExpr *na = (NamedArgExpr *) expr;
na->arg = (Expr *) transformExpr(pstate, (Node *) na->arg);
result = expr;
break;
}
case T_SubLink: case T_SubLink:
result = transformSubLink(pstate, (SubLink *) expr); result = transformSubLink(pstate, (SubLink *) expr);
break; break;
......
This diff is collapsed.
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -131,7 +131,7 @@ regprocin(PG_FUNCTION_ARGS)
* pg_proc entries in the current search path. * pg_proc entries in the current search path.
*/ */
names = stringToQualifiedNameList(pro_name_or_oid); names = stringToQualifiedNameList(pro_name_or_oid);
clist = FuncnameGetCandidates(names, -1, false, false); clist = FuncnameGetCandidates(names, -1, NIL, false, false);
if (clist == NULL) if (clist == NULL)
ereport(ERROR, ereport(ERROR,
...@@ -190,7 +190,7 @@ regprocout(PG_FUNCTION_ARGS) ...@@ -190,7 +190,7 @@ regprocout(PG_FUNCTION_ARGS)
* qualify it. * qualify it.
*/ */
clist = FuncnameGetCandidates(list_make1(makeString(proname)), clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-1, false, false); -1, NIL, false, false);
if (clist != NULL && clist->next == NULL && if (clist != NULL && clist->next == NULL &&
clist->oid == proid) clist->oid == proid)
nspname = NULL; nspname = NULL;
...@@ -277,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS) ...@@ -277,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS)
*/ */
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes); 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) for (; clist; clist = clist->next)
{ {
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -218,8 +218,8 @@ static Node *processIndirection(Node *node, deparse_context *context,
bool printit); bool printit);
static void printSubscripts(ArrayRef *aref, deparse_context *context); static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *generate_relation_name(Oid relid, List *namespaces); static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes, static char *generate_function_name(Oid funcid, int nargs, List *argnames,
bool *is_variadic); Oid *argtypes, bool *is_variadic);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static text *string_to_text(char *str); static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid); static char *flatten_reloptions(Oid relid);
...@@ -558,7 +558,8 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) ...@@ -558,7 +558,8 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
appendStringInfo(&buf, "FOR EACH STATEMENT "); appendStringInfo(&buf, "FOR EACH STATEMENT ");
appendStringInfo(&buf, "EXECUTE PROCEDURE %s(", 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) if (trigrec->tgnargs > 0)
{ {
...@@ -4324,6 +4325,15 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -4324,6 +4325,15 @@ get_rule_expr(Node *node, deparse_context *context,
get_func_expr((FuncExpr *) node, context, showimplicit); get_func_expr((FuncExpr *) node, context, showimplicit);
break; 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: case T_OpExpr:
get_oper_expr((OpExpr *) node, context); get_oper_expr((OpExpr *) node, context);
break; break;
...@@ -5187,6 +5197,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context, ...@@ -5187,6 +5197,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
Oid funcoid = expr->funcid; Oid funcoid = expr->funcid;
Oid argtypes[FUNC_MAX_ARGS]; Oid argtypes[FUNC_MAX_ARGS];
int nargs; int nargs;
List *argnames;
bool is_variadic; bool is_variadic;
ListCell *l; ListCell *l;
...@@ -5231,14 +5242,20 @@ get_func_expr(FuncExpr *expr, deparse_context *context, ...@@ -5231,14 +5242,20 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS), (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg("too many arguments"))); errmsg("too many arguments")));
nargs = 0; nargs = 0;
argnames = NIL;
foreach(l, expr->args) 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++; nargs++;
} }
appendStringInfo(buf, "%s(", appendStringInfo(buf, "%s(",
generate_function_name(funcoid, nargs, argtypes, generate_function_name(funcoid, nargs,
argnames, argtypes,
&is_variadic)); &is_variadic));
nargs = 0; nargs = 0;
foreach(l, expr->args) foreach(l, expr->args)
...@@ -5270,13 +5287,16 @@ get_agg_expr(Aggref *aggref, deparse_context *context) ...@@ -5270,13 +5287,16 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
nargs = 0; nargs = 0;
foreach(l, aggref->args) foreach(l, aggref->args)
{ {
argtypes[nargs] = exprType((Node *) lfirst(l)); Node *arg = (Node *) lfirst(l);
Assert(!IsA(arg, NamedArgExpr));
argtypes[nargs] = exprType(arg);
nargs++; nargs++;
} }
appendStringInfo(buf, "%s(%s", appendStringInfo(buf, "%s(%s",
generate_function_name(aggref->aggfnoid, generate_function_name(aggref->aggfnoid, nargs,
nargs, argtypes, NULL), NIL, argtypes, NULL),
aggref->aggdistinct ? "DISTINCT " : ""); aggref->aggdistinct ? "DISTINCT " : "");
/* aggstar can be set only in zero-argument aggregates */ /* aggstar can be set only in zero-argument aggregates */
if (aggref->aggstar) if (aggref->aggstar)
...@@ -5304,13 +5324,16 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context) ...@@ -5304,13 +5324,16 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
nargs = 0; nargs = 0;
foreach(l, wfunc->args) foreach(l, wfunc->args)
{ {
argtypes[nargs] = exprType((Node *) lfirst(l)); Node *arg = (Node *) lfirst(l);
Assert(!IsA(arg, NamedArgExpr));
argtypes[nargs] = exprType(arg);
nargs++; nargs++;
} }
appendStringInfo(buf, "%s(%s", appendStringInfo(buf, "%s(",
generate_function_name(wfunc->winfnoid, generate_function_name(wfunc->winfnoid, nargs,
nargs, argtypes, NULL), ""); NIL, argtypes, NULL));
/* winstar can be set only in zero-argument aggregates */ /* winstar can be set only in zero-argument aggregates */
if (wfunc->winstar) if (wfunc->winstar)
appendStringInfoChar(buf, '*'); appendStringInfoChar(buf, '*');
...@@ -6338,15 +6361,15 @@ generate_relation_name(Oid relid, List *namespaces) ...@@ -6338,15 +6361,15 @@ generate_relation_name(Oid relid, List *namespaces)
/* /*
* generate_function_name * generate_function_name
* Compute the name to display for a function specified by OID, * Compute the name to display for a function specified by OID,
* given that it is being called with the specified actual arg types. * given that it is being called with the specified actual arg names and
* (Arg types matter because of ambiguous-function resolution rules.) * types. (Those matter because of ambiguous-function resolution rules.)
* *
* The result includes all necessary quoting and schema-prefixing. We can * The result includes all necessary quoting and schema-prefixing. We can
* also pass back an indication of whether the function is variadic. * also pass back an indication of whether the function is variadic.
*/ */
static char * static char *
generate_function_name(Oid funcid, int nargs, Oid *argtypes, generate_function_name(Oid funcid, int nargs, List *argnames,
bool *is_variadic) Oid *argtypes, bool *is_variadic)
{ {
HeapTuple proctup; HeapTuple proctup;
Form_pg_proc procform; Form_pg_proc procform;
...@@ -6371,10 +6394,12 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes, ...@@ -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 * 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 * 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)), 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_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_true_typeids, NULL); &p_retset, &p_nvargs, &p_true_typeids, NULL);
if ((p_result == FUNCDETAIL_NORMAL || if ((p_result == FUNCDETAIL_NORMAL ||
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Copyright (c) 2002-2009, PostgreSQL Global Development Group * Copyright (c) 2002-2009, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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, ...@@ -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 * get_func_result_name
* *
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200910071 #define CATALOG_VERSION_NO 200910072
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -32,6 +32,7 @@ typedef struct _FuncCandidateList
int nargs; /* number of arg types returned */ int nargs; /* number of arg types returned */
int nvargs; /* number of args to become variadic array */ int nvargs; /* number of args to become variadic array */
int ndargs; /* number of defaulted args */ int ndargs; /* number of defaulted args */
int *argnumbers; /* args' positional indexes, if named call */
Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */ Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */
} *FuncCandidateList; /* VARIABLE LENGTH STRUCT */ } *FuncCandidateList; /* VARIABLE LENGTH STRUCT */
...@@ -54,7 +55,8 @@ extern bool RelationIsVisible(Oid relid); ...@@ -54,7 +55,8 @@ extern bool RelationIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname); extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid); 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_variadic,
bool expand_defaults); bool expand_defaults);
extern bool FunctionIsVisible(Oid funcid); extern bool FunctionIsVisible(Oid funcid);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* Copyright (c) 2002-2009, PostgreSQL Global Development Group * 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, ...@@ -173,6 +173,9 @@ extern int get_func_arg_info(HeapTuple procTup,
Oid **p_argtypes, char ***p_argnames, Oid **p_argtypes, char ***p_argnames,
char **p_argmodes); 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 char *get_func_result_name(Oid functionId);
extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes, extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -123,6 +123,7 @@ typedef enum NodeTag
T_WindowFunc, T_WindowFunc,
T_ArrayRef, T_ArrayRef,
T_FuncExpr, T_FuncExpr,
T_NamedArgExpr,
T_OpExpr, T_OpExpr,
T_DistinctExpr, T_DistinctExpr,
T_ScalarArrayOpExpr, T_ScalarArrayOpExpr,
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -313,6 +313,29 @@ typedef struct FuncExpr
int location; /* token location, or -1 if unknown */ int location; /* token location, or -1 if unknown */
} FuncExpr; } 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 * OpExpr - expression node for an operator invocation
* *
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, ...@@ -47,7 +47,8 @@ extern Node *ParseFuncOrColumn(ParseState *pstate,
bool agg_star, bool agg_distinct, bool func_variadic, bool agg_star, bool agg_distinct, bool func_variadic,
WindowDef *over, bool is_column, int location); 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, int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults, bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype, Oid *funcid, Oid *rettype,
...@@ -68,10 +69,10 @@ extern void make_fn_arguments(ParseState *pstate, ...@@ -68,10 +69,10 @@ extern void make_fn_arguments(ParseState *pstate,
Oid *actual_arg_types, Oid *actual_arg_types,
Oid *declared_arg_types); Oid *declared_arg_types);
extern const char *funcname_signature_string(const char *funcname, extern const char *funcname_signature_string(const char *funcname, int nargs,
int nargs, const Oid *argtypes); List *argnames, const Oid *argtypes);
extern const char *func_signature_string(List *funcname, extern const char *func_signature_string(List *funcname, int nargs,
int nargs, const Oid *argtypes); List *argnames, const Oid *argtypes);
extern Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, extern Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
bool noError); bool noError);
......
This diff is collapsed.
...@@ -159,7 +159,7 @@ SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text); ...@@ -159,7 +159,7 @@ SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
1 | 1 | Joe 1 | 1 | Joe
(1 row) (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); (fooid int, foosubid int, fooname text);
SELECT * FROM vw_getfoo; SELECT * FROM vw_getfoo;
fooid | foosubid | fooname fooid | foosubid | fooname
...@@ -515,7 +515,13 @@ SELECT * FROM dup('xyz'::text); ...@@ -515,7 +515,13 @@ SELECT * FROM dup('xyz'::text);
xyz | {xyz,xyz} xyz | {xyz,xyz}
(1 row) (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) CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql; AS 'select $1, array[$1,$1]' LANGUAGE sql;
SELECT dup(22); SELECT dup(22);
......
-- Currently this tests polymorphic aggregates and indirectly does some -- Currently this tests polymorphic aggregates and indirectly does some
-- testing of polymorphic SQL functions. It ought to be extended. -- testing of polymorphic SQL functions. It ought to be extended.
-- Tests for other features related to function-calling have snuck in, too.
-- Legend: -- Legend:
...@@ -21,7 +22,7 @@ ...@@ -21,7 +22,7 @@
-- !> = not allowed -- !> = not allowed
-- E = exists -- E = exists
-- NE = not-exists -- NE = not-exists
-- --
-- Possible states: -- Possible states:
-- ---------------- -- ----------------
-- B = (A || P || N) -- B = (A || P || N)
...@@ -69,7 +70,7 @@ CREATE FUNCTION ffnp(int[]) returns int[] as ...@@ -69,7 +70,7 @@ CREATE FUNCTION ffnp(int[]) returns int[] as
'select $1' LANGUAGE SQL; 'select $1' LANGUAGE SQL;
-- Try to cover all the possible states: -- Try to cover all the possible states:
-- --
-- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn -- 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, -- 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 -- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
...@@ -624,3 +625,123 @@ select dfunc('Hi'); ...@@ -624,3 +625,123 @@ select dfunc('Hi');
drop function dfunc(int, int, int); drop function dfunc(int, int, int);
drop function dfunc(int, int); drop function dfunc(int, int);
drop function dfunc(text); 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);
...@@ -70,7 +70,7 @@ DROP VIEW vw_getfoo; ...@@ -70,7 +70,7 @@ DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int); DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL; 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); 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); (fooid int, foosubid int, fooname text);
SELECT * FROM vw_getfoo; SELECT * FROM vw_getfoo;
...@@ -251,7 +251,13 @@ SELECT dup('xyz'); -- fails ...@@ -251,7 +251,13 @@ SELECT dup('xyz'); -- fails
SELECT dup('xyz'::text); SELECT dup('xyz'::text);
SELECT * FROM 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) CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql; AS 'select $1, array[$1,$1]' LANGUAGE sql;
SELECT dup(22); SELECT dup(22);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment