<!--
-$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">
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>
<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>
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">
-<!-- $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>
(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>
-<!-- $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>
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">
</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>
-<!-- $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>
</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>
</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>
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;
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">
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>
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>
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
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);
* 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
* 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
* 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;
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 */
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
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;
* 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)))
* 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;
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
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- nargs, false, false);
+ nargs, NIL, false, false);
for (; clist; clist = clist->next)
{
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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);
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
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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
/* There is one; okay to replace it? */
Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
+ Datum proargnames;
+ bool isnull;
if (!replace)
ereport(ERROR,
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
if (oldproc->pronargdefaults != 0)
{
Datum proargdefaults;
- bool isnull;
List *oldDefaults;
ListCell *oldlc;
ListCell *newlc;
*
*
* 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
errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname,
procForm->pronargs,
+ NIL,
procForm->proargtypes.values),
get_namespace_name(namespaceOid))));
*
*
* 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
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;
}
errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname,
procForm->pronargs,
+ NIL,
procForm->proargtypes.values),
get_namespace_name(namespaceOid))));
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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);
*
*
* 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
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 */
}
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 */
}
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 */
}
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 */
}
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,
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,
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,
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*/
case T_FuncExpr:
retval = _copyFuncExpr(from);
break;
+ case T_NamedArgExpr:
+ retval = _copyNamedArgExpr(from);
+ break;
case T_OpExpr:
retval = _copyOpExpr(from);
break;
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
break;
+ case T_NamedArgExpr:
+ retval = _equalNamedArgExpr(a, b);
+ break;
case T_OpExpr:
retval = _equalOpExpr(a, b);
break;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
return coercedTypmod;
}
break;
+ case T_NamedArgExpr:
+ return exprTypmod((Node *) ((NamedArgExpr *) expr)->arg);
case T_SubLink:
{
SubLink *sublink = (SubLink *) 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 */
return true;
}
break;
+ case T_NamedArgExpr:
+ return walker(((NamedArgExpr *) node)->arg, context);
case T_OpExpr:
{
OpExpr *expr = (OpExpr *) 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;
/* 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;
*
*
* 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*
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)
{
case T_FuncExpr:
_outFuncExpr(str, obj);
break;
+ case T_NamedArgExpr:
+ _outNamedArgExpr(str, obj);
+ break;
case T_OpExpr:
_outOpExpr(str, obj);
break;
*
*
* 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
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
*/
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))
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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
* 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
{
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 */
*
*
* 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
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,
* 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 *
{
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
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;
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
- true, context);
+ false, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
- false, context);
+ false, false, context);
if (simple) /* successfully simplified it */
{
/*
simple = simplify_function(outfunc,
CSTRINGOID, -1,
&args,
- true, context);
+ false, true, context);
if (simple) /* successfully simplified output fn */
{
/*
simple = simplify_function(infunc,
expr->resulttype, -1,
&args,
- true, context);
+ false, true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
* 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)
{
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,
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,
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int nargsprovided = list_length(args);
- 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,
- Anum_pg_proc_proargdefaults,
- &isnull);
- if (isnull)
- elog(ERROR, "not enough default arguments");
- str = TextDatumGetCString(proargdefaults);
- defaults = (List *) stringToNode(str);
- Assert(IsA(defaults, List));
- pfree(str);
+ /* 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);
- Assert(list_length(args) == funcform->pronargs);
+
+ /* Recheck argument types and add casts if needed */
+ recheck_cast_function_args(args, result_type, func_tuple);
/*
- * The next part should be a no-op if there are no polymorphic arguments,
- * but we do it anyway to be sure.
+ * 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;
+
+ /* The error cases here shouldn't happen, but check anyway */
+ proargdefaults = SysCacheGetAttr(PROCOID, func_tuple,
+ Anum_pg_proc_proargdefaults,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "not enough default arguments");
+ str = TextDatumGetCString(proargdefaults);
+ defaults = (List *) stringToNode(str);
+ Assert(IsA(defaults, List));
+ pfree(str);
+ 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;
+
if (list_length(args) > FUNC_MAX_ARGS)
elog(ERROR, "too many function arguments");
nargs = 0;
{
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,
/* 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;
}
/*
inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
{
FuncExpr *fexpr;
+ Oid func_oid;
HeapTuple func_tuple;
Form_pg_proc funcform;
Oid *argtypes;
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
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);
/*
* 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;
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,
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);
/*
* 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 ||
* 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;
*
*
* 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
%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
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;
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;
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;
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;
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;
}
;
+/* 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); }
;
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
int nargsplusdefs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid *declared_arg_types;
+ List *argnames;
List *argdefaults;
Node *retval;
bool retset;
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);
* 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];
* 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);
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."),
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."),
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);
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);
* 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,
*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);
/*
* 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);
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);
*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)
+ {
+ /*
+ * 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
{
- Datum proargdefaults;
- bool isnull;
- char *str;
- List *defaults;
+ /*
+ * 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;
/* 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++;
}
* 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, ')');
* 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);
}
/*
{
FuncCandidateList clist;
- clist = FuncnameGetCandidates(funcname, nargs, false, false);
+ clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
while (clist)
{
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;
}
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 */
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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,
* 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;
*/
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)
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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)
{
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;
Oid funcoid = expr->funcid;
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
+ List *argnames;
bool is_variadic;
ListCell *l;
(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)
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)
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, '*');
/*
* 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;
/*
* 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 ||
* 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 $
*
*-------------------------------------------------------------------------
*/
}
+/*
+ * 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
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200910071
+#define CATALOG_VERSION_NO 200910072
#endif
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
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);
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
* 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 $
*
*-------------------------------------------------------------------------
*/
T_WindowFunc,
T_ArrayRef,
T_FuncExpr,
+ T_NamedArgExpr,
T_OpExpr,
T_DistinctExpr,
T_ScalarArrayOpExpr,
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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);
-- 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
-- !> = not allowed
-- E = exists
-- NE = not-exists
---
+--
-- Possible states:
-- ----------------
-- B = (A || P || N)
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
-- 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
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
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);
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
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);
-- 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:
-- !> = not allowed
-- E = exists
-- NE = not-exists
---
+--
-- Possible states:
-- ----------------
-- B = (A || P || N)
'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
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);
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;
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);