aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/namespace.c299
-rw-r--r--src/backend/catalog/pg_aggregate.c14
-rw-r--r--src/backend/catalog/pg_proc.c48
-rw-r--r--src/backend/commands/aggregatecmds.c3
-rw-r--r--src/backend/commands/functioncmds.c36
-rw-r--r--src/backend/commands/tsearchcmds.c6
-rw-r--r--src/backend/commands/typecmds.c16
-rw-r--r--src/backend/nodes/copyfuncs.c21
-rw-r--r--src/backend/nodes/equalfuncs.c16
-rw-r--r--src/backend/nodes/nodeFuncs.c30
-rw-r--r--src/backend/nodes/outfuncs.c16
-rw-r--r--src/backend/nodes/readfuncs.c20
-rw-r--r--src/backend/optimizer/plan/planner.c17
-rw-r--r--src/backend/optimizer/util/clauses.c326
-rw-r--r--src/backend/parser/gram.y59
-rw-r--r--src/backend/parser/parse_expr.c11
-rw-r--r--src/backend/parser/parse_func.c302
-rw-r--r--src/backend/utils/adt/regproc.c8
-rw-r--r--src/backend/utils/adt/ruleutils.c63
-rw-r--r--src/backend/utils/fmgr/funcapi.c88
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/namespace.h6
-rw-r--r--src/include/funcapi.h5
-rw-r--r--src/include/nodes/nodes.h3
-rw-r--r--src/include/nodes/primnodes.h25
-rw-r--r--src/include/parser/parse_func.h13
-rw-r--r--src/test/regress/expected/polymorphism.out334
-rw-r--r--src/test/regress/expected/rangefuncs.out10
-rw-r--r--src/test/regress/sql/polymorphism.sql125
-rw-r--r--src/test/regress/sql/rangefuncs.sql10
30 files changed, 1672 insertions, 262 deletions
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 12cb1720cde..2c327d45f15 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.118 2009/06/11 14:48:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.119 2009/10/08 02:39:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,6 +36,7 @@
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
+#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
@@ -188,6 +189,8 @@ static void InitTempTableNamespace(void);
static void RemoveTempRelations(Oid tempNamespaceId);
static void RemoveTempRelationsCallback(int code, Datum arg);
static void NamespaceCallback(Datum arg, int cacheid, ItemPointer tuplePtr);
+static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
+ int **argnumbers);
/* These don't really need to appear in any header file */
Datum pg_table_is_visible(PG_FUNCTION_ARGS);
@@ -562,8 +565,14 @@ TypeIsVisible(Oid typid)
* retrieve a list of the possible matches.
*
* If nargs is -1, we return all functions matching the given name,
- * regardless of argument count. (expand_variadic and expand_defaults must be
- * false in this case.)
+ * regardless of argument count. (argnames must be NIL, and expand_variadic
+ * and expand_defaults must be false, in this case.)
+ *
+ * If argnames isn't NIL, we are considering a named- or mixed-notation call,
+ * and only functions having all the listed argument names will be returned.
+ * (We assume that length(argnames) <= nargs and all the passed-in names are
+ * distinct.) The returned structs will include an argnumbers array showing
+ * the actual argument index for each logical argument position.
*
* If expand_variadic is true, then variadic functions having the same number
* or fewer arguments will be retrieved, with the variadic argument and any
@@ -583,6 +592,13 @@ TypeIsVisible(Oid typid)
* than nargs arguments while the variadic transformation requires the same
* number or less.
*
+ * When argnames isn't NIL, the returned args[] type arrays are not ordered
+ * according to the functions' declarations, but rather according to the call:
+ * first any positional arguments, then the named arguments, then defaulted
+ * arguments (if needed and allowed by expand_defaults). The argnumbers[]
+ * array can be used to map this back to the catalog information.
+ * argnumbers[k] is set to the proargtypes index of the k'th call argument.
+ *
* We search a single namespace if the function name is qualified, else
* all namespaces in the search path. In the multiple-namespace case,
* we arrange for entries in earlier namespaces to mask identical entries in
@@ -596,15 +612,16 @@ TypeIsVisible(Oid typid)
* It is guaranteed that the return list will never contain multiple entries
* with identical argument lists. When expand_defaults is true, the entries
* could have more than nargs positions, but we still guarantee that they are
- * distinct in the first nargs positions. However, if either expand_variadic
- * or expand_defaults is true, there might be multiple candidate functions
- * that expand to identical argument lists. Rather than throw error here,
- * we report such situations by setting oid = 0 in the ambiguous entries.
+ * distinct in the first nargs positions. However, if argnames isn't NIL or
+ * either expand_variadic or expand_defaults is true, there might be multiple
+ * candidate functions that expand to identical argument lists. Rather than
+ * throw error here, we report such situations by returning a single entry
+ * with oid = 0 that represents a set of such conflicting candidates.
* The caller might end up discarding such an entry anyway, but if it selects
* such an entry it should react as though the call were ambiguous.
*/
FuncCandidateList
-FuncnameGetCandidates(List *names, int nargs,
+FuncnameGetCandidates(List *names, int nargs, List *argnames,
bool expand_variadic, bool expand_defaults)
{
FuncCandidateList resultList = NULL;
@@ -648,42 +665,9 @@ FuncnameGetCandidates(List *names, int nargs,
bool variadic;
bool use_defaults;
Oid va_elem_type;
+ int *argnumbers = NULL;
FuncCandidateList newResult;
- /*
- * Check if function is variadic, and get variadic element type if so.
- * If expand_variadic is false, we should just ignore variadic-ness.
- */
- if (pronargs <= nargs && expand_variadic)
- {
- va_elem_type = procform->provariadic;
- variadic = OidIsValid(va_elem_type);
- any_special |= variadic;
- }
- else
- {
- va_elem_type = InvalidOid;
- variadic = false;
- }
-
- /*
- * Check if function can match by using parameter defaults.
- */
- if (pronargs > nargs && expand_defaults)
- {
- /* Ignore if not enough default expressions */
- if (nargs + procform->pronargdefaults < pronargs)
- continue;
- use_defaults = true;
- any_special = true;
- }
- else
- use_defaults = false;
-
- /* Ignore if it doesn't match requested argument count */
- if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
- continue;
-
if (OidIsValid(namespaceId))
{
/* Consider only procs in specified namespace */
@@ -709,6 +693,88 @@ FuncnameGetCandidates(List *names, int nargs,
continue; /* proc is not in search path */
}
+ if (argnames != NIL)
+ {
+ /*
+ * Call uses named or mixed notation
+ *
+ * Named or mixed notation can match a variadic function only
+ * if expand_variadic is off; otherwise there is no way to match
+ * the presumed-nameless parameters expanded from the variadic
+ * array.
+ */
+ if (OidIsValid(procform->provariadic) && expand_variadic)
+ continue;
+ va_elem_type = InvalidOid;
+ variadic = false;
+
+ /*
+ * Check argument count.
+ */
+ Assert(nargs >= 0); /* -1 not supported with argnames */
+
+ if (pronargs > nargs && expand_defaults)
+ {
+ /* Ignore if not enough default expressions */
+ if (nargs + procform->pronargdefaults < pronargs)
+ continue;
+ use_defaults = true;
+ }
+ else
+ use_defaults = false;
+
+ /* Ignore if it doesn't match requested argument count */
+ if (pronargs != nargs && !use_defaults)
+ continue;
+
+ /* Check for argument name match, generate positional mapping */
+ if (!MatchNamedCall(proctup, nargs, argnames,
+ &argnumbers))
+ continue;
+
+ /* Named argument matching is always "special" */
+ any_special = true;
+ }
+ else
+ {
+ /*
+ * Call uses positional notation
+ *
+ * Check if function is variadic, and get variadic element type if
+ * so. If expand_variadic is false, we should just ignore
+ * variadic-ness.
+ */
+ if (pronargs <= nargs && expand_variadic)
+ {
+ va_elem_type = procform->provariadic;
+ variadic = OidIsValid(va_elem_type);
+ any_special |= variadic;
+ }
+ else
+ {
+ va_elem_type = InvalidOid;
+ variadic = false;
+ }
+
+ /*
+ * Check if function can match by using parameter defaults.
+ */
+ if (pronargs > nargs && expand_defaults)
+ {
+ /* Ignore if not enough default expressions */
+ if (nargs + procform->pronargdefaults < pronargs)
+ continue;
+ use_defaults = true;
+ any_special = true;
+ }
+ else
+ use_defaults = false;
+
+ /* Ignore if it doesn't match requested argument count */
+ if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
+ continue;
+ }
+
/*
* We must compute the effective argument list so that we can easily
* compare it to earlier results. We waste a palloc cycle if it gets
@@ -722,8 +788,22 @@ FuncnameGetCandidates(List *names, int nargs,
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = effective_nargs;
- memcpy(newResult->args, procform->proargtypes.values,
- pronargs * sizeof(Oid));
+ newResult->argnumbers = argnumbers;
+ if (argnumbers)
+ {
+ /* Re-order the argument types into call's logical order */
+ Oid *proargtypes = procform->proargtypes.values;
+ int i;
+
+ for (i = 0; i < pronargs; i++)
+ newResult->args[i] = proargtypes[argnumbers[i]];
+ }
+ else
+ {
+ /* Simple positional case, just copy proargtypes as-is */
+ memcpy(newResult->args, procform->proargtypes.values,
+ pronargs * sizeof(Oid));
+ }
if (variadic)
{
int i;
@@ -741,9 +821,9 @@ FuncnameGetCandidates(List *names, int nargs,
* Does it have the same arguments as something we already accepted?
* If so, decide what to do to avoid returning duplicate argument
* lists. We can skip this check for the single-namespace case if no
- * special (variadic or defaults) match has been made, since then the
- * unique index on pg_proc guarantees all the matches have different
- * argument lists.
+ * special (named, variadic or defaults) match has been made, since
+ * then the unique index on pg_proc guarantees all the matches have
+ * different argument lists.
*/
if (resultList != NULL &&
(any_special || !OidIsValid(namespaceId)))
@@ -827,7 +907,8 @@ FuncnameGetCandidates(List *names, int nargs,
* both foo(numeric, variadic numeric[]) and
* foo(variadic numeric[]) in the same namespace, or
* both foo(int) and foo (int, int default something)
- * in the same namespace.
+ * in the same namespace, or both foo(a int, b text)
+ * and foo(b text, a int) in the same namespace.
*----------
*/
preference = 0;
@@ -886,6 +967,125 @@ FuncnameGetCandidates(List *names, int nargs,
}
/*
+ * 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
* current search path. Visible means "would be found by searching
@@ -932,7 +1132,7 @@ FunctionIsVisible(Oid funcid)
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- nargs, false, false);
+ nargs, NIL, false, false);
for (; clist; clist = clist->next)
{
@@ -1202,6 +1402,7 @@ OpernameGetCandidates(List *names, char oprkind)
newResult->nargs = 2;
newResult->nvargs = 0;
newResult->ndargs = 0;
+ newResult->argnumbers = NULL;
newResult->args[0] = operform->oprleft;
newResult->args[1] = operform->oprright;
newResult->next = resultList;
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 8c9f8bce7a7..7fae4b66b4d 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.102 2009/06/11 14:48:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.103 2009/10/08 02:39:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -321,7 +321,8 @@ lookup_agg_function(List *fnName,
* function's return value. it also returns the true argument types to
* the function.
*/
- fdresult = func_get_detail(fnName, NIL, nargs, input_types, false, false,
+ fdresult = func_get_detail(fnName, NIL, NIL,
+ nargs, input_types, false, false,
&fnOid, rettype, &retset, &nvargs,
&true_oid_array, NULL);
@@ -330,12 +331,14 @@ lookup_agg_function(List *fnName,
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(fnName, nargs, input_types))));
+ func_signature_string(fnName, nargs,
+ NIL, input_types))));
if (retset)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s returns a set",
- func_signature_string(fnName, nargs, input_types))));
+ func_signature_string(fnName, nargs,
+ NIL, input_types))));
/*
* If there are any polymorphic types involved, enforce consistency, and
@@ -359,7 +362,8 @@ lookup_agg_function(List *fnName,
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function %s requires run-time type coercion",
- func_signature_string(fnName, nargs, true_oid_array))));
+ func_signature_string(fnName, nargs,
+ NIL, true_oid_array))));
}
/* Check aggregate creator has permission to call the function */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index db69c127ad4..c7ee17a57b7 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.167 2009/10/05 19:24:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.168 2009/10/08 02:39:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -348,6 +348,8 @@ ProcedureCreate(const char *procedureName,
{
/* There is one; okay to replace it? */
Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
+ Datum proargnames;
+ bool isnull;
if (!replace)
ereport(ERROR,
@@ -394,6 +396,49 @@ ProcedureCreate(const char *procedureName,
}
/*
+ * 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
* default might cause a function to fail to satisfy an existing call.
@@ -404,7 +449,6 @@ ProcedureCreate(const char *procedureName,
if (oldproc->pronargdefaults != 0)
{
Datum proargdefaults;
- bool isnull;
List *oldDefaults;
ListCell *oldlc;
ListCell *newlc;
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 461f81005c2..bafc2856606 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.49 2009/06/11 14:48:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.50 2009/10/08 02:39:18 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -297,6 +297,7 @@ RenameAggregate(List *name, List *args, const char *newname)
errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname,
procForm->pronargs,
+ NIL,
procForm->proargtypes.values),
get_namespace_name(namespaceOid))));
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index cf206b3f090..40097a80c72 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.111 2009/09/22 23:43:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.112 2009/10/08 02:39:19 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
@@ -285,6 +285,39 @@ examine_parameter_list(List *parameters, Oid languageOid,
if (fp->name && fp->name[0])
{
+ ListCell *px;
+
+ /*
+ * As of Postgres 8.5 we disallow using the same name for two
+ * input or two output function parameters. Depending on the
+ * function's language, conflicting input and output names might
+ * be bad too, but we leave it to the PL to complain if so.
+ */
+ foreach(px, parameters)
+ {
+ FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
+
+ if (prevfp == fp)
+ break;
+ /* pure in doesn't conflict with pure out */
+ if ((fp->mode == FUNC_PARAM_IN ||
+ fp->mode == FUNC_PARAM_VARIADIC) &&
+ (prevfp->mode == FUNC_PARAM_OUT ||
+ prevfp->mode == FUNC_PARAM_TABLE))
+ continue;
+ if ((prevfp->mode == FUNC_PARAM_IN ||
+ prevfp->mode == FUNC_PARAM_VARIADIC) &&
+ (fp->mode == FUNC_PARAM_OUT ||
+ fp->mode == FUNC_PARAM_TABLE))
+ continue;
+ if (prevfp->name && prevfp->name[0] &&
+ strcmp(prevfp->name, fp->name) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("parameter name \"%s\" used more than once",
+ fp->name)));
+ }
+
paramNames[i] = CStringGetTextDatum(fp->name);
have_names = true;
}
@@ -1097,6 +1130,7 @@ RenameFunction(List *name, List *argtypes, const char *newname)
errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname,
procForm->pronargs,
+ NIL,
procForm->proargtypes.values),
get_namespace_name(namespaceOid))));
}
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 5339e1783c4..8696ea85730 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.17 2009/06/11 14:48:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.18 2009/10/08 02:39:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -107,7 +107,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("function %s should return type %s",
- func_signature_string(funcName, nargs, typeId),
+ func_signature_string(funcName, nargs, NIL, typeId),
format_type_be(retTypeId))));
return ObjectIdGetDatum(procOid);
@@ -945,7 +945,7 @@ get_ts_template_func(DefElem *defel, int attnum)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("function %s should return type %s",
- func_signature_string(funcName, nargs, typeId),
+ func_signature_string(funcName, nargs, NIL, typeId),
format_type_be(retTypeId))));
return ObjectIdGetDatum(procOid);
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 5fa2ac6bc90..4f997a0dce1 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.137 2009/07/30 02:45:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.138 2009/10/08 02:39:19 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -1267,7 +1267,7 @@ findTypeInputFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(procname, 1, argList))));
+ func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */
}
@@ -1318,7 +1318,7 @@ findTypeOutputFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(procname, 1, argList))));
+ func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */
}
@@ -1349,7 +1349,7 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(procname, 1, argList))));
+ func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */
}
@@ -1372,7 +1372,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(procname, 1, argList))));
+ func_signature_string(procname, 1, NIL, argList))));
return InvalidOid; /* keep compiler quiet */
}
@@ -1393,7 +1393,7 @@ findTypeTypmodinFunction(List *procname)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(procname, 1, argList))));
+ func_signature_string(procname, 1, NIL, argList))));
if (get_func_rettype(procOid) != INT4OID)
ereport(ERROR,
@@ -1420,7 +1420,7 @@ findTypeTypmodoutFunction(List *procname)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(procname, 1, argList))));
+ func_signature_string(procname, 1, NIL, argList))));
if (get_func_rettype(procOid) != CSTRINGOID)
ereport(ERROR,
@@ -1447,7 +1447,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(procname, 1, argList))));
+ func_signature_string(procname, 1, NIL, argList))));
if (get_func_rettype(procOid) != BOOLOID)
ereport(ERROR,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9319aa84c5e..27c231701d6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.441 2009/10/07 22:14:20 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.442 2009/10/08 02:39:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1020,6 +1020,22 @@ _copyFuncExpr(FuncExpr *from)
}
/*
+ * _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
*/
static OpExpr *
@@ -3587,6 +3603,9 @@ copyObject(void *from)
case T_FuncExpr:
retval = _copyFuncExpr(from);
break;
+ case T_NamedArgExpr:
+ retval = _copyNamedArgExpr(from);
+ break;
case T_OpExpr:
retval = _copyOpExpr(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6a61112b99c..5766f37c143 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.364 2009/10/07 22:14:20 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.365 2009/10/08 02:39:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -242,6 +242,17 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
}
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)
{
COMPARE_SCALAR_FIELD(opno);
@@ -2375,6 +2386,9 @@ equal(void *a, void *b)
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
break;
+ case T_NamedArgExpr:
+ retval = _equalNamedArgExpr(a, b);
+ break;
case T_OpExpr:
retval = _equalOpExpr(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 61c0e21db08..04a39f17526 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.42 2009/07/30 02:45:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.43 2009/10/08 02:39:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -69,6 +69,9 @@ exprType(Node *expr)
case T_FuncExpr:
type = ((FuncExpr *) expr)->funcresulttype;
break;
+ case T_NamedArgExpr:
+ type = exprType((Node *) ((NamedArgExpr *) expr)->arg);
+ break;
case T_OpExpr:
type = ((OpExpr *) expr)->opresulttype;
break;
@@ -259,6 +262,8 @@ exprTypmod(Node *expr)
return coercedTypmod;
}
break;
+ case T_NamedArgExpr:
+ return exprTypmod((Node *) ((NamedArgExpr *) expr)->arg);
case T_SubLink:
{
SubLink *sublink = (SubLink *) expr;
@@ -676,6 +681,15 @@ exprLocation(Node *expr)
exprLocation((Node *) fexpr->args));
}
break;
+ case T_NamedArgExpr:
+ {
+ NamedArgExpr *na = (NamedArgExpr *) expr;
+
+ /* consider both argument name and value */
+ loc = leftmostLoc(na->location,
+ exprLocation((Node *) na->arg));
+ }
+ break;
case T_OpExpr:
case T_DistinctExpr: /* struct-equivalent to OpExpr */
case T_NullIfExpr: /* struct-equivalent to OpExpr */
@@ -1117,6 +1131,8 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_NamedArgExpr:
+ return walker(((NamedArgExpr *) node)->arg, context);
case T_OpExpr:
{
OpExpr *expr = (OpExpr *) node;
@@ -1623,6 +1639,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_NamedArgExpr:
+ {
+ NamedArgExpr *nexpr = (NamedArgExpr *) node;
+ NamedArgExpr *newnode;
+
+ FLATCOPY(newnode, nexpr, NamedArgExpr);
+ MUTATE(newnode->arg, nexpr->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
case T_OpExpr:
{
OpExpr *expr = (OpExpr *) node;
@@ -2382,6 +2408,8 @@ bool
/* function name is deemed uninteresting */
}
break;
+ case T_NamedArgExpr:
+ return walker(((NamedArgExpr *) node)->arg, context);
case T_A_Indices:
{
A_Indices *indices = (A_Indices *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6751cb1a344..79665ed12a0 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.365 2009/10/06 00:55:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.366 2009/10/08 02:39:21 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@@ -876,6 +876,17 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
}
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)
{
WRITE_NODE_TYPE("OPEXPR");
@@ -2514,6 +2525,9 @@ _outNode(StringInfo str, void *obj)
case T_FuncExpr:
_outFuncExpr(str, obj);
break;
+ case T_NamedArgExpr:
+ _outNamedArgExpr(str, obj);
+ break;
case T_OpExpr:
_outOpExpr(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 8f5264d3575..205b6da4cd8 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.223 2009/07/16 06:33:42 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.224 2009/10/08 02:39:21 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@@ -526,6 +526,22 @@ _readFuncExpr(void)
}
/*
+ * _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
*/
static OpExpr *
@@ -1207,6 +1223,8 @@ parseNodeString(void)
return_value = _readArrayRef();
else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr();
+ else if (MATCH("NAMEDARGEXPR", 12))
+ return_value = _readNamedArgExpr();
else if (MATCH("OPEXPR", 6))
return_value = _readOpExpr();
else if (MATCH("DISTINCTEXPR", 12))
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 3f344b3a145..64f77a5c71e 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.256 2009/06/11 14:48:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.257 2009/10/08 02:39:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -526,7 +526,8 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
/*
* Simplify constant expressions.
*
- * Note: one essential effect here is to insert the current actual values
+ * Note: an essential effect of this is to convert named-argument function
+ * calls to positional notation and insert the current actual values
* of any default arguments for functions. To ensure that happens, we
* *must* process all expressions here. Previous PG versions sometimes
* skipped const-simplification if it didn't seem worth the trouble, but
@@ -2658,9 +2659,10 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
* Currently, we disallow sublinks in standalone expressions, so there's no
* real "planning" involved here. (That might not always be true though.)
* What we must do is run eval_const_expressions to ensure that any function
- * default arguments get inserted. The fact that constant subexpressions
- * get simplified is a side-effect that is useful when the expression will
- * get evaluated more than once. Also, we must fix operator function IDs.
+ * calls are converted to positional notation and function default arguments
+ * get inserted. The fact that constant subexpressions get simplified is a
+ * side-effect that is useful when the expression will get evaluated more than
+ * once. Also, we must fix operator function IDs.
*
* Note: this must not make any damaging changes to the passed-in expression
* tree. (It would actually be okay to apply fix_opfuncids to it, but since
@@ -2672,7 +2674,10 @@ expression_planner(Expr *expr)
{
Node *result;
- /* Insert default arguments and simplify constant subexprs */
+ /*
+ * Convert named-argument function calls, insert default arguments and
+ * simplify constant subexprs
+ */
result = eval_const_expressions(NULL, (Node *) expr);
/* Fill in opfuncid values if missing */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f2038c73af1..dcfc731a17a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.278 2009/07/20 00:24:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.279 2009/10/08 02:39:21 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -95,11 +95,18 @@ static List *simplify_and_arguments(List *args,
static Expr *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod, List **args,
+ bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context);
+static List *reorder_function_arguments(List *args, Oid result_type,
+ HeapTuple func_tuple,
+ eval_const_expressions_context *context);
static List *add_function_defaults(List *args, Oid result_type,
HeapTuple func_tuple,
eval_const_expressions_context *context);
+static List *fetch_function_defaults(HeapTuple func_tuple);
+static void recheck_cast_function_args(List *args, Oid result_type,
+ HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
HeapTuple func_tuple,
@@ -2003,7 +2010,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
* OR clauses into N-argument form. See comments in prepqual.c.
*
* NOTE: another critical effect is that any function calls that require
- * default arguments will be expanded.
+ * default arguments will be expanded, and named-argument calls will be
+ * converted to positional notation. The executor won't handle either.
*--------------------
*/
Node *
@@ -2113,17 +2121,26 @@ eval_const_expressions_mutator(Node *node,
{
FuncExpr *expr = (FuncExpr *) node;
List *args;
+ bool has_named_args;
Expr *simple;
FuncExpr *newexpr;
+ ListCell *lc;
/*
- * Reduce constants in the FuncExpr's arguments. We know args is
- * either NIL or a List node, so we can call expression_tree_mutator
- * directly rather than recursing to self.
+ * Reduce constants in the FuncExpr's arguments, and check to see
+ * if there are any named args.
*/
- args = (List *) expression_tree_mutator((Node *) expr->args,
- eval_const_expressions_mutator,
- (void *) context);
+ args = NIL;
+ has_named_args = false;
+ foreach(lc, expr->args)
+ {
+ Node *arg = (Node *) lfirst(lc);
+
+ arg = eval_const_expressions_mutator(arg, context);
+ if (IsA(arg, NamedArgExpr))
+ has_named_args = true;
+ args = lappend(args, arg);
+ }
/*
* Code for op/func reduction is pretty bulky, so split it out as a
@@ -2134,14 +2151,15 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
&args,
- true, context);
+ has_named_args, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
/*
* The expression cannot be simplified any further, so build and
* return a replacement FuncExpr node using the possibly-simplified
- * arguments.
+ * arguments. Note that we have also converted the argument list
+ * to positional notation.
*/
newexpr = makeNode(FuncExpr);
newexpr->funcid = expr->funcid;
@@ -2181,7 +2199,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
- true, context);
+ false, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
@@ -2274,7 +2292,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
- false, context);
+ false, false, context);
if (simple) /* successfully simplified it */
{
/*
@@ -2466,7 +2484,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(outfunc,
CSTRINGOID, -1,
&args,
- true, context);
+ false, true, context);
if (simple) /* successfully simplified output fn */
{
/*
@@ -2484,7 +2502,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(infunc,
expr->resulttype, -1,
&args,
- true, context);
+ false, true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
@@ -3241,15 +3259,16 @@ simplify_boolean_equality(Oid opno, List *args)
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function call.
*
- * This function is also responsible for adding any default argument
- * expressions onto the function argument list; which is a bit grotty,
- * but it avoids an extra fetch of the function's pg_proc tuple. For this
- * reason, the args list is pass-by-reference, and it may get modified
- * even if simplification fails.
+ * This function is also responsible for converting named-notation argument
+ * lists into positional notation and/or adding any needed default argument
+ * expressions; which is a bit grotty, but it avoids an extra fetch of the
+ * function's pg_proc tuple. For this reason, the args list is
+ * pass-by-reference, and it may get modified even if simplification fails.
*/
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
List **args,
+ bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context)
{
@@ -3270,8 +3289,14 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
- /* While we have the tuple, check if we need to add defaults */
- if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
+ /*
+ * While we have the tuple, reorder named arguments and add default
+ * arguments if needed.
+ */
+ if (has_named_args)
+ *args = reorder_function_arguments(*args, result_type, func_tuple,
+ context);
+ else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
*args = add_function_defaults(*args, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
@@ -3287,12 +3312,112 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
}
/*
+ * reorder_function_arguments: convert named-notation args to positional args
+ *
+ * This function also inserts default argument values as needed, since it's
+ * impossible to form a truly valid positional call without that.
+ */
+static List *
+reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
+ eval_const_expressions_context *context)
+{
+ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+ int pronargs = funcform->pronargs;
+ int nargsprovided = list_length(args);
+ Node *argarray[FUNC_MAX_ARGS];
+ Bitmapset *defargnumbers;
+ ListCell *lc;
+ int i;
+
+ Assert(nargsprovided <= pronargs);
+ if (pronargs > FUNC_MAX_ARGS)
+ elog(ERROR, "too many function arguments");
+ MemSet(argarray, 0, pronargs * sizeof(Node *));
+
+ /* Deconstruct the argument list into an array indexed by argnumber */
+ i = 0;
+ foreach(lc, args)
+ {
+ Node *arg = (Node *) lfirst(lc);
+
+ if (!IsA(arg, NamedArgExpr))
+ {
+ /* positional argument, assumed to precede all named args */
+ Assert(argarray[i] == NULL);
+ argarray[i++] = arg;
+ }
+ else
+ {
+ NamedArgExpr *na = (NamedArgExpr *) arg;
+
+ Assert(argarray[na->argnumber] == NULL);
+ argarray[na->argnumber] = (Node *) na->arg;
+ }
+ }
+
+ /*
+ * Fetch default expressions, if needed, and insert into array at
+ * proper locations (they aren't necessarily consecutive or all used)
+ */
+ defargnumbers = NULL;
+ if (nargsprovided < pronargs)
+ {
+ List *defaults = fetch_function_defaults(func_tuple);
+
+ i = pronargs - funcform->pronargdefaults;
+ foreach(lc, defaults)
+ {
+ if (argarray[i] == NULL)
+ {
+ argarray[i] = (Node *) lfirst(lc);
+ defargnumbers = bms_add_member(defargnumbers, i);
+ }
+ i++;
+ }
+ }
+
+ /* Now reconstruct the args list in proper order */
+ args = NIL;
+ for (i = 0; i < pronargs; i++)
+ {
+ Assert(argarray[i] != NULL);
+ args = lappend(args, argarray[i]);
+ }
+
+ /* Recheck argument types and add casts if needed */
+ recheck_cast_function_args(args, result_type, func_tuple);
+
+ /*
+ * Lastly, we have to recursively simplify the defaults we just added
+ * (but don't recurse on the args passed in, as we already did those).
+ * This isn't merely an optimization, it's *necessary* since there could
+ * be functions with named or defaulted arguments down in there.
+ *
+ * Note that we do this last in hopes of simplifying any typecasts that
+ * were added by recheck_cast_function_args --- there shouldn't be any new
+ * casts added to the explicit arguments, but casts on the defaults are
+ * possible.
+ */
+ if (defargnumbers != NULL)
+ {
+ i = 0;
+ foreach(lc, args)
+ {
+ if (bms_is_member(i, defargnumbers))
+ lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
+ context);
+ i++;
+ }
+ }
+
+ return args;
+}
+
+/*
* add_function_defaults: add missing function arguments from its defaults
*
- * It is possible for some of the defaulted arguments to be polymorphic;
- * therefore we can't assume that the default expressions have the correct
- * data types already. We have to re-resolve polymorphics and do coercion
- * just like the parser did.
+ * This is used only when the argument list was positional to begin with,
+ * and so we know we just need to add defaults at the end.
*/
static List *
add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
@@ -3300,41 +3425,96 @@ 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;
@@ -3342,6 +3522,7 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
{
actual_arg_types[nargs++] = exprType((Node *) lfirst(lc));
}
+ Assert(nargs == funcform->pronargs);
memcpy(declared_arg_types, funcform->proargtypes.values,
funcform->pronargs * sizeof(Oid));
rettype = enforce_generic_type_consistency(actual_arg_types,
@@ -3355,22 +3536,6 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
/* perform any necessary typecasting of arguments */
make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types);
-
- /*
- * Lastly, we have to recursively simplify the arguments we just added
- * (but don't recurse on the ones passed in, as we already did those).
- * This isn't merely an optimization, it's *necessary* since there could
- * be functions with defaulted arguments down in there.
- */
- foreach(lc, args)
- {
- if (nargsprovided-- > 0)
- continue; /* skip original arg positions */
- lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
- context);
- }
-
- return args;
}
/*
@@ -3916,6 +4081,7 @@ Query *
inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
{
FuncExpr *fexpr;
+ Oid func_oid;
HeapTuple func_tuple;
Form_pg_proc funcform;
Oid *argtypes;
@@ -3944,6 +4110,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
fexpr = (FuncExpr *) rte->funcexpr;
if (fexpr == NULL || !IsA(fexpr, FuncExpr))
return NULL;
+ func_oid = fexpr->funcid;
/*
* The function must be declared to return a set, else inlining would
@@ -3967,17 +4134,17 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
return NULL;
/* Check permission to call function (fail later, if not) */
- if (pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
+ if (pg_proc_aclcheck(func_oid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
return NULL;
/*
* OK, let's take a look at the function's pg_proc entry.
*/
func_tuple = SearchSysCache(PROCOID,
- ObjectIdGetDatum(fexpr->funcid),
+ ObjectIdGetDatum(func_oid),
0, 0, 0);
if (!HeapTupleIsValid(func_tuple))
- elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
+ elog(ERROR, "cache lookup failed for function %u", func_oid);
funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
/*
@@ -3985,16 +4152,15 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* properties. In particular it mustn't be declared STRICT, since we
* couldn't enforce that. It also mustn't be VOLATILE, because that is
* supposed to cause it to be executed with its own snapshot, rather than
- * sharing the snapshot of the calling query. (The nargs check is just
- * paranoia, ditto rechecking proretset.)
+ * sharing the snapshot of the calling query. (Rechecking proretset is
+ * just paranoia.)
*/
if (funcform->prolang != SQLlanguageId ||
funcform->proisstrict ||
funcform->provolatile == PROVOLATILE_VOLATILE ||
funcform->prosecdef ||
!funcform->proretset ||
- !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
- funcform->pronargs != list_length(fexpr->args))
+ !heap_attisnull(func_tuple, Anum_pg_proc_proconfig))
{
ReleaseSysCache(func_tuple);
return NULL;
@@ -4020,6 +4186,24 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(mycxt);
+ /*
+ * Run eval_const_expressions on the function call. This is necessary
+ * to ensure that named-argument notation is converted to positional
+ * notation and any default arguments are inserted. It's a bit of
+ * overkill for the arguments, since they'll get processed again later,
+ * but no harm will be done.
+ */
+ fexpr = (FuncExpr *) eval_const_expressions(root, (Node *) fexpr);
+
+ /* It should still be a call of the same function, but let's check */
+ if (!IsA(fexpr, FuncExpr) ||
+ fexpr->funcid != func_oid)
+ goto fail;
+
+ /* Arg list length should now match the function */
+ if (list_length(fexpr->args) != funcform->pronargs)
+ goto fail;
+
/* Check for polymorphic arguments, and substitute actual arg types */
argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
memcpy(argtypes, funcform->proargtypes.values,
@@ -4038,7 +4222,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
Anum_pg_proc_prosrc,
&isNull);
if (isNull)
- elog(ERROR, "null prosrc for function %u", fexpr->funcid);
+ elog(ERROR, "null prosrc for function %u", func_oid);
src = TextDatumGetCString(tmp);
/*
@@ -4076,7 +4260,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* shows it's returning a whole tuple result; otherwise what it's
* returning is a single composite column which is not what we need.
*/
- if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype,
+ if (!check_sql_fn_retval(func_oid, fexpr->funcresulttype,
querytree_list,
true, NULL) &&
(get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE ||
@@ -4116,7 +4300,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* Since there is now no trace of the function in the plan tree, we must
* explicitly record the plan's dependency on the function.
*/
- record_plan_function_dependency(root->glob, fexpr->funcid);
+ record_plan_function_dependency(root->glob, func_oid);
return querytree;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ed265f516b2..b86df8a870f 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.681 2009/10/07 22:14:21 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.682 2009/10/08 02:39:22 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -354,6 +354,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <node> def_arg columnElem where_clause where_or_current_clause
a_expr b_expr c_expr func_expr AexprConst indirection_el
columnref in_expr having_clause func_table array_expr
+%type <list> func_arg_list
+%type <node> func_arg_expr
%type <list> row type_list array_expr_list
%type <node> case_expr case_arg when_clause case_default
%type <list> when_clause_list
@@ -9055,7 +9057,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
- | func_name '(' expr_list ')' over_clause
+ | func_name '(' func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@@ -9067,7 +9069,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
- | func_name '(' VARIADIC a_expr ')' over_clause
+ | func_name '(' VARIADIC func_arg_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@@ -9079,7 +9081,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
- | func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause
+ | func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@@ -9091,7 +9093,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
- | func_name '(' ALL expr_list ')' over_clause
+ | func_name '(' ALL func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@@ -9107,7 +9109,7 @@ func_expr: func_name '(' ')' over_clause
n->location = @1;
$$ = (Node *)n;
}
- | func_name '(' DISTINCT expr_list ')' over_clause
+ | func_name '(' DISTINCT func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
@@ -9830,6 +9832,32 @@ expr_list: a_expr
}
;
+/* function arguments can have names */
+func_arg_list: func_arg_expr
+ {
+ $$ = list_make1($1);
+ }
+ | func_arg_list ',' func_arg_expr
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
+func_arg_expr: a_expr
+ {
+ $$ = $1;
+ }
+ | a_expr AS param_name
+ {
+ NamedArgExpr *na = makeNode(NamedArgExpr);
+ na->arg = (Expr *) $1;
+ na->name = $3;
+ na->argnumber = -1; /* until determined */
+ na->location = @3;
+ $$ = (Node *) na;
+ }
+ ;
+
type_list: Typename { $$ = list_make1($1); }
| type_list ',' Typename { $$ = lappend($1, $3); }
;
@@ -10296,10 +10324,27 @@ AexprConst: Iconst
t->location = @1;
$$ = makeStringConstCast($2, @2, t);
}
- | func_name '(' expr_list ')' Sconst
+ | func_name '(' func_arg_list ')' Sconst
{
/* generic syntax with a type modifier */
TypeName *t = makeTypeNameFromNameList($1);
+ ListCell *lc;
+
+ /*
+ * We must use func_arg_list in the production to avoid
+ * reduce/reduce conflicts, but we don't actually wish
+ * to allow NamedArgExpr in this context.
+ */
+ foreach(lc, $3)
+ {
+ NamedArgExpr *arg = (NamedArgExpr *) lfirst(lc);
+
+ if (IsA(arg, NamedArgExpr))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("type modifier cannot have AS name"),
+ parser_errposition(arg->location)));
+ }
t->typmods = $3;
t->location = @1;
$$ = makeStringConstCast($5, @5, t);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 39d66dcc12f..3fccba1ab5a 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.243 2009/09/09 03:32:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.244 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -228,6 +228,15 @@ transformExpr(ParseState *pstate, Node *expr)
result = transformFuncCall(pstate, (FuncCall *) expr);
break;
+ case T_NamedArgExpr:
+ {
+ NamedArgExpr *na = (NamedArgExpr *) expr;
+
+ na->arg = (Expr *) transformExpr(pstate, (Node *) na->arg);
+ result = expr;
+ break;
+ }
+
case T_SubLink:
result = transformSubLink(pstate, (SubLink *) expr);
break;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index fd0706e9608..e752dd8d1ec 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.216 2009/06/11 14:49:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.217 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -70,6 +70,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
int nargsplusdefs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid *declared_arg_types;
+ List *argnames;
List *argdefaults;
Node *retval;
bool retset;
@@ -117,6 +118,46 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
actual_arg_types[nargs++] = argtype;
}
+ /*
+ * Check for named arguments; if there are any, build a list of names.
+ *
+ * We allow mixed notation (some named and some not), but only with all
+ * the named parameters after all the unnamed ones. So the name list
+ * corresponds to the last N actual parameters and we don't need any
+ * extra bookkeeping to match things up.
+ */
+ argnames = NIL;
+ foreach(l, fargs)
+ {
+ Node *arg = lfirst(l);
+
+ if (IsA(arg, NamedArgExpr))
+ {
+ NamedArgExpr *na = (NamedArgExpr *) arg;
+ ListCell *lc;
+
+ /* Reject duplicate arg names */
+ foreach(lc, argnames)
+ {
+ if (strcmp(na->name, (char *) lfirst(lc)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("argument name \"%s\" used more than once",
+ na->name),
+ parser_errposition(pstate, na->location)));
+ }
+ argnames = lappend(argnames, na->name);
+ }
+ else
+ {
+ if (argnames != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("positional argument cannot follow named argument"),
+ parser_errposition(pstate, exprLocation(arg))));
+ }
+ }
+
if (fargs)
{
first_arg = linitial(fargs);
@@ -127,10 +168,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* Check for column projection: if function has one argument, and that
* argument is of complex type, and function name is not qualified, then
* the "function call" could be a projection. We also check that there
- * wasn't any aggregate or variadic decoration.
+ * wasn't any aggregate or variadic decoration, nor an argument name.
*/
if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
- !func_variadic && list_length(funcname) == 1)
+ !func_variadic && argnames == NIL && list_length(funcname) == 1)
{
Oid argtype = actual_arg_types[0];
@@ -156,12 +197,17 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* disambiguation for polymorphic functions, handles inheritance, and
* returns the funcid and type and set or singleton status of the
* function's return value. It also returns the true argument types to
- * the function. In the case of a variadic function call, the reported
- * "true" types aren't really what is in pg_proc: the variadic argument is
- * replaced by a suitable number of copies of its element type. We'll fix
- * it up below. We may also have to deal with default arguments.
+ * the function.
+ *
+ * Note: for a named-notation or variadic function call, the reported
+ * "true" types aren't really what is in pg_proc: the types are reordered
+ * to match the given argument order of named arguments, and a variadic
+ * argument is replaced by a suitable number of copies of its element
+ * type. We'll fix up the variadic case below. We may also have to deal
+ * with default arguments.
*/
- fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
+ fdresult = func_get_detail(funcname, fargs, argnames, nargs,
+ actual_arg_types,
!func_variadic, true,
&funcid, &rettype, &retset, &nvargs,
&declared_arg_types, &argdefaults);
@@ -225,7 +271,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function %s is not unique",
- func_signature_string(funcname, nargs,
+ func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
errhint("Could not choose a best candidate function. "
"You might need to add explicit type casts."),
@@ -234,7 +280,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(funcname, nargs,
+ func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
errhint("No function matches the given name and argument types. "
"You might need to add explicit type casts."),
@@ -353,6 +399,18 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("aggregates cannot return sets"),
parser_errposition(pstate, location)));
+ /*
+ * Currently it's not possible to define an aggregate with named
+ * arguments, so this case should be impossible. Check anyway
+ * because the planner and executor wouldn't cope with NamedArgExprs
+ * in an Aggref node.
+ */
+ if (argnames != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("aggregates cannot use named arguments"),
+ parser_errposition(pstate, location)));
+
/* parse_agg.c does additional aggregate-specific processing */
transformAggregateCall(pstate, aggref);
@@ -406,6 +464,17 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("window functions cannot return sets"),
parser_errposition(pstate, location)));
+ /*
+ * We might want to support this later, but for now reject it
+ * because the planner and executor wouldn't cope with NamedArgExprs
+ * in a WindowFunc node.
+ */
+ if (argnames != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("window functions cannot use named arguments"),
+ parser_errposition(pstate, location)));
+
/* parse_agg.c does additional window-func-specific processing */
transformWindowFuncCall(pstate, wfunc, over);
@@ -801,14 +870,29 @@ func_select_candidate(int nargs,
* 1) check for possible interpretation as a type coercion request
* 2) apply the ambiguous-function resolution rules
*
- * Note: we rely primarily on nargs/argtypes as the argument description.
+ * Return values *funcid through *true_typeids receive info about the function.
+ * If argdefaults isn't NULL, *argdefaults receives a list of any default
+ * argument expressions that need to be added to the given arguments.
+ *
+ * When processing a named- or mixed-notation call (ie, fargnames isn't NIL),
+ * the returned true_typeids and argdefaults are ordered according to the
+ * call's argument ordering: first any positional arguments, then the named
+ * arguments, then defaulted arguments (if needed and allowed by
+ * expand_defaults). Some care is needed if this information is to be compared
+ * to the function's pg_proc entry, but in practice the caller can usually
+ * just work with the call's argument ordering.
+ *
+ * We rely primarily on fargnames/nargs/argtypes as the argument description.
* The actual expression node list is passed in fargs so that we can check
- * for type coercion of a constant. Some callers pass fargs == NIL
- * indicating they don't want that check made.
+ * for type coercion of a constant. Some callers pass fargs == NIL indicating
+ * they don't need that check made. Note also that when fargnames isn't NIL,
+ * the fargs list must be passed if the caller wants actual argument position
+ * information to be returned into the NamedArgExpr nodes.
*/
FuncDetailCode
func_get_detail(List *funcname,
List *fargs,
+ List *fargnames,
int nargs,
Oid *argtypes,
bool expand_variadic,
@@ -833,7 +917,7 @@ func_get_detail(List *funcname,
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
- raw_candidates = FuncnameGetCandidates(funcname, nargs,
+ raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
expand_variadic, expand_defaults);
/*
@@ -884,7 +968,7 @@ func_get_detail(List *funcname,
* coerce_type can't handle, we'll cause infinite recursion between
* this module and coerce_type!
*/
- if (nargs == 1 && fargs != NIL)
+ if (nargs == 1 && fargs != NIL && fargnames == NIL)
{
Oid targetType = FuncNameAsType(funcname);
@@ -967,17 +1051,47 @@ func_get_detail(List *funcname,
FuncDetailCode result;
/*
- * If expanding variadics or defaults, the "best candidate" might
- * represent multiple equivalently good functions; treat this case as
- * ambiguous.
+ * If processing named args or expanding variadics or defaults, the
+ * "best candidate" might represent multiple equivalently good
+ * functions; treat this case as ambiguous.
*/
if (!OidIsValid(best_candidate->oid))
return FUNCDETAIL_MULTIPLE;
+ /*
+ * We disallow VARIADIC with named arguments unless the last
+ * argument (the one with VARIADIC attached) actually matched the
+ * variadic parameter. This is mere pedantry, really, but some
+ * folks insisted.
+ */
+ if (fargnames != NIL && !expand_variadic && nargs > 0 &&
+ best_candidate->argnumbers[nargs - 1] != nargs - 1)
+ return FUNCDETAIL_NOTFOUND;
+
*funcid = best_candidate->oid;
*nvargs = best_candidate->nvargs;
*true_typeids = best_candidate->args;
+ /*
+ * If processing named args, return actual argument positions into
+ * NamedArgExpr nodes in the fargs list. This is a bit ugly but not
+ * worth the extra notation needed to do it differently.
+ */
+ if (best_candidate->argnumbers != NULL)
+ {
+ int i = 0;
+ ListCell *lc;
+
+ foreach(lc, fargs)
+ {
+ NamedArgExpr *na = (NamedArgExpr *) lfirst(lc);
+
+ if (IsA(na, NamedArgExpr))
+ na->argnumber = best_candidate->argnumbers[i];
+ i++;
+ }
+ }
+
ftup = SearchSysCache(PROCOID,
ObjectIdGetDatum(best_candidate->oid),
0, 0, 0);
@@ -988,36 +1102,73 @@ func_get_detail(List *funcname,
*rettype = pform->prorettype;
*retset = pform->proretset;
/* fetch default args if caller wants 'em */
- if (argdefaults)
+ if (argdefaults && best_candidate->ndargs > 0)
{
- if (best_candidate->ndargs > 0)
+ Datum proargdefaults;
+ bool isnull;
+ char *str;
+ List *defaults;
+
+ /* shouldn't happen, FuncnameGetCandidates messed up */
+ if (best_candidate->ndargs > pform->pronargdefaults)
+ elog(ERROR, "not enough default arguments");
+
+ proargdefaults = SysCacheGetAttr(PROCOID, ftup,
+ Anum_pg_proc_proargdefaults,
+ &isnull);
+ Assert(!isnull);
+ str = TextDatumGetCString(proargdefaults);
+ defaults = (List *) stringToNode(str);
+ Assert(IsA(defaults, List));
+ pfree(str);
+
+ /* Delete any unused defaults from the returned list */
+ if (best_candidate->argnumbers != NULL)
+ {
+ /*
+ * 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;
@@ -1060,13 +1211,36 @@ make_fn_arguments(ParseState *pstate,
/* types don't match? then force coercion using a function call... */
if (actual_arg_types[i] != declared_arg_types[i])
{
- lfirst(current_fargs) = coerce_type(pstate,
- lfirst(current_fargs),
- actual_arg_types[i],
- declared_arg_types[i], -1,
- COERCION_IMPLICIT,
- COERCE_IMPLICIT_CAST,
- -1);
+ Node *node = (Node *) lfirst(current_fargs);
+
+ /*
+ * If arg is a NamedArgExpr, coerce its input expr instead ---
+ * we want the NamedArgExpr to stay at the top level of the list.
+ */
+ if (IsA(node, NamedArgExpr))
+ {
+ NamedArgExpr *na = (NamedArgExpr *) node;
+
+ node = coerce_type(pstate,
+ (Node *) na->arg,
+ actual_arg_types[i],
+ declared_arg_types[i], -1,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ na->arg = (Expr *) node;
+ }
+ else
+ {
+ node = coerce_type(pstate,
+ node,
+ actual_arg_types[i],
+ declared_arg_types[i], -1,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ lfirst(current_fargs) = node;
+ }
}
i++;
}
@@ -1223,25 +1397,39 @@ unknown_attribute(ParseState *pstate, Node *relref, char *attname,
* Build a string representing a function name, including arg types.
* The result is something like "foo(integer)".
*
+ * If argnames isn't NIL, it is a list of C strings representing the actual
+ * arg names for the last N arguments. This must be considered part of the
+ * function signature too, when dealing with named-notation function calls.
+ *
* This is typically used in the construction of function-not-found error
* messages.
*/
const char *
-funcname_signature_string(const char *funcname,
- int nargs, const Oid *argtypes)
+funcname_signature_string(const char *funcname, int nargs,
+ List *argnames, const Oid *argtypes)
{
StringInfoData argbuf;
+ int numposargs;
+ ListCell *lc;
int i;
initStringInfo(&argbuf);
appendStringInfo(&argbuf, "%s(", funcname);
+ numposargs = nargs - list_length(argnames);
+ lc = list_head(argnames);
+
for (i = 0; i < nargs; i++)
{
if (i)
appendStringInfoString(&argbuf, ", ");
appendStringInfoString(&argbuf, format_type_be(argtypes[i]));
+ if (i >= numposargs)
+ {
+ appendStringInfo(&argbuf, " AS %s", (char *) lfirst(lc));
+ lc = lnext(lc);
+ }
}
appendStringInfoChar(&argbuf, ')');
@@ -1254,10 +1442,11 @@ funcname_signature_string(const char *funcname,
* As above, but function name is passed as a qualified name list.
*/
const char *
-func_signature_string(List *funcname, int nargs, const Oid *argtypes)
+func_signature_string(List *funcname, int nargs,
+ List *argnames, const Oid *argtypes)
{
return funcname_signature_string(NameListToString(funcname),
- nargs, argtypes);
+ nargs, argnames, argtypes);
}
/*
@@ -1276,7 +1465,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
FuncCandidateList clist;
- clist = FuncnameGetCandidates(funcname, nargs, false, false);
+ clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
while (clist)
{
@@ -1289,7 +1478,8 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
- func_signature_string(funcname, nargs, argtypes))));
+ func_signature_string(funcname, nargs,
+ NIL, argtypes))));
return InvalidOid;
}
@@ -1401,8 +1591,8 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("aggregate %s does not exist",
- func_signature_string(aggname,
- argcount, argoids))));
+ func_signature_string(aggname, argcount,
+ NIL, argoids))));
}
/* Make sure it's an aggregate */
@@ -1422,8 +1612,8 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("function %s is not an aggregate",
- func_signature_string(aggname,
- argcount, argoids))));
+ func_signature_string(aggname, argcount,
+ NIL, argoids))));
}
ReleaseSysCache(ftup);
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index fd90a290220..dd2a53f5a5f 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.110 2009/01/01 17:23:49 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.111 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -131,7 +131,7 @@ regprocin(PG_FUNCTION_ARGS)
* pg_proc entries in the current search path.
*/
names = stringToQualifiedNameList(pro_name_or_oid);
- clist = FuncnameGetCandidates(names, -1, false, false);
+ clist = FuncnameGetCandidates(names, -1, NIL, false, false);
if (clist == NULL)
ereport(ERROR,
@@ -190,7 +190,7 @@ regprocout(PG_FUNCTION_ARGS)
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- -1, false, false);
+ -1, NIL, false, false);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
@@ -277,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS)
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
- clist = FuncnameGetCandidates(names, nargs, false, false);
+ clist = FuncnameGetCandidates(names, nargs, NIL, false, false);
for (; clist; clist = clist->next)
{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 38057a0bfdc..4c04bafd7c6 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.306 2009/08/01 19:59:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.307 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -218,8 +218,8 @@ static Node *processIndirection(Node *node, deparse_context *context,
bool printit);
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *generate_relation_name(Oid relid, List *namespaces);
-static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes,
- bool *is_variadic);
+static char *generate_function_name(Oid funcid, int nargs, List *argnames,
+ Oid *argtypes, bool *is_variadic);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
@@ -558,7 +558,8 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
appendStringInfo(&buf, "FOR EACH STATEMENT ");
appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
- generate_function_name(trigrec->tgfoid, 0, NULL, NULL));
+ generate_function_name(trigrec->tgfoid, 0,
+ NIL, NULL, NULL));
if (trigrec->tgnargs > 0)
{
@@ -4324,6 +4325,15 @@ get_rule_expr(Node *node, deparse_context *context,
get_func_expr((FuncExpr *) node, context, showimplicit);
break;
+ case T_NamedArgExpr:
+ {
+ NamedArgExpr *na = (NamedArgExpr *) node;
+
+ get_rule_expr((Node *) na->arg, context, showimplicit);
+ appendStringInfo(buf, " AS %s", quote_identifier(na->name));
+ }
+ break;
+
case T_OpExpr:
get_oper_expr((OpExpr *) node, context);
break;
@@ -5187,6 +5197,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
Oid funcoid = expr->funcid;
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
+ List *argnames;
bool is_variadic;
ListCell *l;
@@ -5231,14 +5242,20 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg("too many arguments")));
nargs = 0;
+ argnames = NIL;
foreach(l, expr->args)
{
- argtypes[nargs] = exprType((Node *) lfirst(l));
+ Node *arg = (Node *) lfirst(l);
+
+ if (IsA(arg, NamedArgExpr))
+ argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
+ argtypes[nargs] = exprType(arg);
nargs++;
}
appendStringInfo(buf, "%s(",
- generate_function_name(funcoid, nargs, argtypes,
+ generate_function_name(funcoid, nargs,
+ argnames, argtypes,
&is_variadic));
nargs = 0;
foreach(l, expr->args)
@@ -5270,13 +5287,16 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
nargs = 0;
foreach(l, aggref->args)
{
- argtypes[nargs] = exprType((Node *) lfirst(l));
+ Node *arg = (Node *) lfirst(l);
+
+ Assert(!IsA(arg, NamedArgExpr));
+ argtypes[nargs] = exprType(arg);
nargs++;
}
appendStringInfo(buf, "%s(%s",
- generate_function_name(aggref->aggfnoid,
- nargs, argtypes, NULL),
+ generate_function_name(aggref->aggfnoid, nargs,
+ NIL, argtypes, NULL),
aggref->aggdistinct ? "DISTINCT " : "");
/* aggstar can be set only in zero-argument aggregates */
if (aggref->aggstar)
@@ -5304,13 +5324,16 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
nargs = 0;
foreach(l, wfunc->args)
{
- argtypes[nargs] = exprType((Node *) lfirst(l));
+ Node *arg = (Node *) lfirst(l);
+
+ Assert(!IsA(arg, NamedArgExpr));
+ argtypes[nargs] = exprType(arg);
nargs++;
}
- appendStringInfo(buf, "%s(%s",
- generate_function_name(wfunc->winfnoid,
- nargs, argtypes, NULL), "");
+ appendStringInfo(buf, "%s(",
+ generate_function_name(wfunc->winfnoid, nargs,
+ NIL, argtypes, NULL));
/* winstar can be set only in zero-argument aggregates */
if (wfunc->winstar)
appendStringInfoChar(buf, '*');
@@ -6338,15 +6361,15 @@ generate_relation_name(Oid relid, List *namespaces)
/*
* generate_function_name
* Compute the name to display for a function specified by OID,
- * given that it is being called with the specified actual arg types.
- * (Arg types matter because of ambiguous-function resolution rules.)
+ * given that it is being called with the specified actual arg names and
+ * types. (Those matter because of ambiguous-function resolution rules.)
*
* The result includes all necessary quoting and schema-prefixing. We can
* also pass back an indication of whether the function is variadic.
*/
static char *
-generate_function_name(Oid funcid, int nargs, Oid *argtypes,
- bool *is_variadic)
+generate_function_name(Oid funcid, int nargs, List *argnames,
+ Oid *argtypes, bool *is_variadic)
{
HeapTuple proctup;
Form_pg_proc procform;
@@ -6371,10 +6394,12 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes,
/*
* The idea here is to schema-qualify only if the parser would fail to
* resolve the correct function given the unqualified func name with the
- * specified argtypes.
+ * specified argtypes. If the function is variadic, we should presume
+ * that VARIADIC will be included in the call.
*/
p_result = func_get_detail(list_make1(makeString(proname)),
- NIL, nargs, argtypes, false, true,
+ NIL, argnames, nargs, argtypes,
+ !OidIsValid(procform->provariadic), true,
&p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_true_typeids, NULL);
if ((p_result == FUNCDETAIL_NORMAL ||
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 7ecd7812206..313b777d04b 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -7,7 +7,7 @@
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.45 2009/06/11 14:49:05 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.46 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -767,6 +767,92 @@ get_func_arg_info(HeapTuple procTup,
/*
+ * get_func_input_arg_names
+ *
+ * Extract the names of input arguments only, given a function's
+ * proargnames and proargmodes entries in Datum form.
+ *
+ * Returns the number of input arguments, which is the length of the
+ * palloc'd array returned to *arg_names. Entries for unnamed args
+ * are set to NULL. You don't get anything if proargnames is NULL.
+ */
+int
+get_func_input_arg_names(Datum proargnames, Datum proargmodes,
+ char ***arg_names)
+{
+ ArrayType *arr;
+ int numargs;
+ Datum *argnames;
+ char *argmodes;
+ char **inargnames;
+ int numinargs;
+ int i;
+
+ /* Do nothing if null proargnames */
+ if (proargnames == PointerGetDatum(NULL))
+ {
+ *arg_names = NULL;
+ return 0;
+ }
+
+ /*
+ * We expect the arrays to be 1-D arrays of the right types; verify that.
+ * For proargmodes, we don't need to use deconstruct_array()
+ * since the array data is just going to look like a C array of values.
+ */
+ arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != TEXTOID)
+ elog(ERROR, "proargnames is not a 1-D text array");
+ deconstruct_array(arr, TEXTOID, -1, false, 'i',
+ &argnames, NULL, &numargs);
+ if (proargmodes != PointerGetDatum(NULL))
+ {
+ arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numargs ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "proargmodes is not a 1-D char array");
+ argmodes = (char *) ARR_DATA_PTR(arr);
+ }
+ else
+ argmodes = NULL;
+
+ /* zero elements probably shouldn't happen, but handle it gracefully */
+ if (numargs <= 0)
+ {
+ *arg_names = NULL;
+ return 0;
+ }
+
+ /* extract input-argument names */
+ inargnames = (char **) palloc(numargs * sizeof(char *));
+ numinargs = 0;
+ for (i = 0; i < numargs; i++)
+ {
+ if (argmodes == NULL ||
+ argmodes[i] == PROARGMODE_IN ||
+ argmodes[i] == PROARGMODE_INOUT ||
+ argmodes[i] == PROARGMODE_VARIADIC)
+ {
+ char *pname = TextDatumGetCString(argnames[i]);
+
+ if (pname[0] != '\0')
+ inargnames[numinargs] = pname;
+ else
+ inargnames[numinargs] = NULL;
+ numinargs++;
+ }
+ }
+
+ *arg_names = inargnames;
+ return numinargs;
+}
+
+
+/*
* get_func_result_name
*
* If the function has exactly one output parameter, and that parameter
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 20eac6aa1c7..8959997ea96 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.542 2009/10/07 22:14:24 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.543 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200910071
+#define CATALOG_VERSION_NO 200910072
#endif
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index ed9218c03a4..2c2b88951a3 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.59 2009/06/11 14:49:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.60 2009/10/08 02:39:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,6 +32,7 @@ typedef struct _FuncCandidateList
int nargs; /* number of arg types returned */
int nvargs; /* number of args to become variadic array */
int ndargs; /* number of defaulted args */
+ int *argnumbers; /* args' positional indexes, if named call */
Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */
} *FuncCandidateList; /* VARIABLE LENGTH STRUCT */
@@ -54,7 +55,8 @@ extern bool RelationIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
-extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
+extern FuncCandidateList FuncnameGetCandidates(List *names,
+ int nargs, List *argnames,
bool expand_variadic,
bool expand_defaults);
extern bool FunctionIsVisible(Oid funcid);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 1373e4ad245..b4fe22c492b 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -9,7 +9,7 @@
*
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.29 2009/06/11 14:49:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.30 2009/10/08 02:39:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -173,6 +173,9 @@ extern int get_func_arg_info(HeapTuple procTup,
Oid **p_argtypes, char ***p_argnames,
char **p_argmodes);
+extern int get_func_input_arg_names(Datum proargnames, Datum proargmodes,
+ char ***arg_names);
+
extern char *get_func_result_name(Oid functionId);
extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5fd046e95b8..2a4468799f9 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.227 2009/10/05 19:24:48 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.228 2009/10/08 02:39:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -123,6 +123,7 @@ typedef enum NodeTag
T_WindowFunc,
T_ArrayRef,
T_FuncExpr,
+ T_NamedArgExpr,
T_OpExpr,
T_DistinctExpr,
T_ScalarArrayOpExpr,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 5f5d4125c65..0320e231553 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.150 2009/07/16 06:33:45 petere Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.151 2009/10/08 02:39:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -314,6 +314,29 @@ typedef struct FuncExpr
} FuncExpr;
/*
+ * NamedArgExpr - a named argument of a function
+ *
+ * This node type can only appear in the args list of a FuncCall or FuncExpr
+ * node. We support pure positional call notation (no named arguments),
+ * named notation (all arguments are named), and mixed notation (unnamed
+ * arguments followed by named ones).
+ *
+ * Parse analysis sets argnumber to the positional index of the argument,
+ * but doesn't rearrange the argument list.
+ *
+ * The planner will convert argument lists to pure positional notation
+ * during expression preprocessing, so execution never sees a NamedArgExpr.
+ */
+typedef struct NamedArgExpr
+{
+ Expr xpr;
+ Expr *arg; /* the argument expression */
+ char *name; /* the name */
+ int argnumber; /* argument's number in positional notation */
+ int location; /* argument name location, or -1 if unknown */
+} NamedArgExpr;
+
+/*
* OpExpr - expression node for an operator invocation
*
* Semantically, this is essentially the same as a function call.
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 7905f96e86c..0a38f740b1c 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.65 2009/05/12 00:56:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.66 2009/10/08 02:39:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,7 +47,8 @@ extern Node *ParseFuncOrColumn(ParseState *pstate,
bool agg_star, bool agg_distinct, bool func_variadic,
WindowDef *over, bool is_column, int location);
-extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
+extern FuncDetailCode func_get_detail(List *funcname,
+ List *fargs, List *fargnames,
int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype,
@@ -68,10 +69,10 @@ extern void make_fn_arguments(ParseState *pstate,
Oid *actual_arg_types,
Oid *declared_arg_types);
-extern const char *funcname_signature_string(const char *funcname,
- int nargs, const Oid *argtypes);
-extern const char *func_signature_string(List *funcname,
- int nargs, const Oid *argtypes);
+extern const char *funcname_signature_string(const char *funcname, int nargs,
+ List *argnames, const Oid *argtypes);
+extern const char *func_signature_string(List *funcname, int nargs,
+ List *argnames, const Oid *argtypes);
extern Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
bool noError);
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 77f693c2b14..36b31f09f43 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -1,5 +1,6 @@
-- Currently this tests polymorphic aggregates and indirectly does some
-- testing of polymorphic SQL functions. It ought to be extended.
+-- Tests for other features related to function-calling have snuck in, too.
-- Legend:
-----------
-- A = type is ANY
@@ -19,7 +20,7 @@
-- !> = not allowed
-- E = exists
-- NE = not-exists
---
+--
-- Possible states:
-- ----------------
-- B = (A || P || N)
@@ -60,7 +61,7 @@ CREATE FUNCTION ffp(anyarray) RETURNS anyarray AS
CREATE FUNCTION ffnp(int[]) returns int[] as
'select $1' LANGUAGE SQL;
-- Try to cover all the possible states:
---
+--
-- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn
-- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp,
-- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
@@ -837,7 +838,7 @@ select dfunc();
-- verify it lists properly
\df dfunc
- List of functions
+ List of functions
Schema | Name | Result data type | Argument data types | Type
--------+-------+------------------+-----------------------------------------------------------+--------
public | dfunc | integer | a integer DEFAULT 1, OUT sum integer, b integer DEFAULT 2 | normal
@@ -1005,7 +1006,7 @@ $$ select array_upper($1, 1) $$ language sql;
ERROR: cannot remove parameter defaults from existing function
HINT: Use DROP FUNCTION first.
\df dfunc
- List of functions
+ List of functions
Schema | Name | Result data type | Argument data types | Type
--------+-------+------------------+-------------------------------------------------+--------
public | dfunc | integer | VARIADIC a integer[] DEFAULT ARRAY[]::integer[] | normal
@@ -1038,3 +1039,328 @@ select dfunc('Hi');
drop function dfunc(int, int, int);
drop function dfunc(int, int);
drop function dfunc(text);
+--
+-- Tests for named- and mixed-notation function calling
+--
+create function dfunc(a int, b int, c int = 0, d int = 0)
+ returns table (a int, b int, c int, d int) as $$
+ select $1, $2, $3, $4;
+$$ language sql;
+select (dfunc(10,20,30)).*;
+ a | b | c | d
+----+----+----+---
+ 10 | 20 | 30 | 0
+(1 row)
+
+select (dfunc(10 as a, 20 as b, 30 as c)).*;
+ a | b | c | d
+----+----+----+---
+ 10 | 20 | 30 | 0
+(1 row)
+
+select * from dfunc(10 as a, 20 as b);
+ a | b | c | d
+----+----+---+---
+ 10 | 20 | 0 | 0
+(1 row)
+
+select * from dfunc(10 as b, 20 as a);
+ a | b | c | d
+----+----+---+---
+ 20 | 10 | 0 | 0
+(1 row)
+
+select * from dfunc(0); -- fail
+ERROR: function dfunc(integer) does not exist
+LINE 1: select * from dfunc(0);
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select * from dfunc(1,2);
+ a | b | c | d
+---+---+---+---
+ 1 | 2 | 0 | 0
+(1 row)
+
+select * from dfunc(1,2,3 as c);
+ a | b | c | d
+---+---+---+---
+ 1 | 2 | 3 | 0
+(1 row)
+
+select * from dfunc(1,2,3 as d);
+ a | b | c | d
+---+---+---+---
+ 1 | 2 | 0 | 3
+(1 row)
+
+select * from dfunc(10 as x, 20 as b, 30 as x); -- fail, duplicate name
+ERROR: argument name "x" used more than once
+LINE 1: select * from dfunc(10 as x, 20 as b, 30 as x);
+ ^
+select * from dfunc(10, 20 as b, 30); -- fail, named args must be last
+ERROR: positional argument cannot follow named argument
+LINE 1: select * from dfunc(10, 20 as b, 30);
+ ^
+select * from dfunc(10 as x, 20 as b, 30 as c); -- fail, unknown param
+ERROR: function dfunc(integer AS x, integer AS b, integer AS c) does not exist
+LINE 1: select * from dfunc(10 as x, 20 as b, 30 as c);
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select * from dfunc(10, 10, 20 as a); -- fail, a overlaps positional parameter
+ERROR: function dfunc(integer, integer, integer AS a) does not exist
+LINE 1: select * from dfunc(10, 10, 20 as a);
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select * from dfunc(1,2 as c,3 as d); -- fail, no value for b
+ERROR: function dfunc(integer, integer AS c, integer AS d) does not exist
+LINE 1: select * from dfunc(1,2 as c,3 as d);
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function dfunc(int, int, int, int);
+-- test with different parameter types
+create function dfunc(a varchar, b numeric, c date = current_date)
+ returns table (a varchar, b numeric, c date) as $$
+ select $1, $2, $3;
+$$ language sql;
+select (dfunc('Hello World', 20, '2009-07-25'::date)).*;
+ a | b | c
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('Hello World', 20, '2009-07-25'::date);
+ a | b | c
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('2009-07-25'::date as c, 'Hello World' as a, 20 as b);
+ a | b | c
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('Hello World', 20 as b, '2009-07-25'::date as c);
+ a | b | c
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('Hello World', '2009-07-25'::date as c, 20 as b);
+ a | b | c
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('Hello World', 20 as c, '2009-07-25'::date as b); -- fail
+ERROR: function dfunc(unknown, integer AS c, date AS b) does not exist
+LINE 1: select * from dfunc('Hello World', 20 as c, '2009-07-25'::da...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function dfunc(varchar, numeric, date);
+-- test out parameters with named params
+create function dfunc(a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric)
+returns record as $$
+ select $1, $2;
+$$ language sql;
+select (dfunc()).*;
+ _a | _c
+-------+----
+ def a |
+(1 row)
+
+select * from dfunc();
+ _a | _c
+-------+----
+ def a |
+(1 row)
+
+select * from dfunc('Hello', 100);
+ _a | _c
+-------+-----
+ Hello | 100
+(1 row)
+
+select * from dfunc('Hello' as a, 100 as c);
+ _a | _c
+-------+-----
+ Hello | 100
+(1 row)
+
+select * from dfunc(100 as c, 'Hello' as a);
+ _a | _c
+-------+-----
+ Hello | 100
+(1 row)
+
+select * from dfunc('Hello');
+ _a | _c
+-------+----
+ Hello |
+(1 row)
+
+select * from dfunc('Hello', 100 as c);
+ _a | _c
+-------+-----
+ Hello | 100
+(1 row)
+
+select * from dfunc(100 as c);
+ _a | _c
+-------+-----
+ def a | 100
+(1 row)
+
+-- fail, can no longer change an input parameter's name
+create or replace function dfunc(a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric)
+returns record as $$
+ select $1, $2;
+$$ language sql;
+ERROR: cannot change name of input parameter "c"
+HINT: Use DROP FUNCTION first.
+create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric)
+returns record as $$
+ select $1, $2;
+$$ language sql;
+ERROR: cannot change name of input parameter "c"
+HINT: Use DROP FUNCTION first.
+drop function dfunc(varchar, numeric);
+--fail, named parameters are not unique
+create function testfoo(a int, a int) returns int as $$ select 1;$$ language sql;
+ERROR: parameter name "a" used more than once
+create function testfoo(int, out a int, out a int) returns int as $$ select 1;$$ language sql;
+ERROR: parameter name "a" used more than once
+create function testfoo(out a int, inout a int) returns int as $$ select 1;$$ language sql;
+ERROR: parameter name "a" used more than once
+create function testfoo(a int, inout a int) returns int as $$ select 1;$$ language sql;
+ERROR: parameter name "a" used more than once
+-- valid
+create function testfoo(a int, out a int) returns int as $$ select $1;$$ language sql;
+select testfoo(37);
+ testfoo
+---------
+ 37
+(1 row)
+
+drop function testfoo(int);
+create function testfoo(a int) returns table(a int) as $$ select $1;$$ language sql;
+select * from testfoo(37);
+ a
+----
+ 37
+(1 row)
+
+drop function testfoo(int);
+-- test polymorphic params and defaults
+create function dfunc(a anyelement, b anyelement = null, flag bool = true)
+returns anyelement as $$
+ select case when $3 then $1 else $2 end;
+$$ language sql;
+select dfunc(1,2);
+ dfunc
+-------
+ 1
+(1 row)
+
+select dfunc('a'::text, 'b'); -- positional notation with default
+ dfunc
+-------
+ a
+(1 row)
+
+select dfunc(1 as a, 2 as b);
+ dfunc
+-------
+ 1
+(1 row)
+
+select dfunc('a'::text as a, 'b' as b);
+ dfunc
+-------
+ a
+(1 row)
+
+select dfunc('a'::text as a, 'b' as b, false as flag); -- named notation
+ dfunc
+-------
+ b
+(1 row)
+
+select dfunc('b'::text as b, 'a' as a); -- named notation with default
+ dfunc
+-------
+ a
+(1 row)
+
+select dfunc('a'::text as a, true as flag); -- named notation with default
+ dfunc
+-------
+ a
+(1 row)
+
+select dfunc('a'::text as a, false as flag); -- named notation with default
+ dfunc
+-------
+
+(1 row)
+
+select dfunc('b'::text as b, 'a' as a, true as flag); -- named notation
+ dfunc
+-------
+ a
+(1 row)
+
+select dfunc('a'::text, 'b', false); -- full positional notation
+ dfunc
+-------
+ b
+(1 row)
+
+select dfunc('a'::text, 'b', false as flag); -- mixed notation
+ dfunc
+-------
+ b
+(1 row)
+
+select dfunc('a'::text, 'b', true); -- full positional notation
+ dfunc
+-------
+ a
+(1 row)
+
+select dfunc('a'::text, 'b', true as flag); -- mixed notation
+ dfunc
+-------
+ a
+(1 row)
+
+-- check reverse-listing of named-arg calls
+CREATE VIEW dfview AS
+ SELECT q1, q2,
+ dfunc(q1,q2, q1>q2 as flag) as c3,
+ dfunc(q1, q1<q2 as flag, q2 AS b) as c4
+ FROM int8_tbl;
+select * from dfview;
+ q1 | q2 | c3 | c4
+------------------+-------------------+------------------+-------------------
+ 123 | 456 | 456 | 123
+ 123 | 4567890123456789 | 4567890123456789 | 123
+ 4567890123456789 | 123 | 4567890123456789 | 123
+ 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789
+ 4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789
+(5 rows)
+
+\d dfview
+ View "public.dfview"
+ Column | Type | Modifiers
+--------+--------+-----------
+ q1 | bigint |
+ q2 | bigint |
+ c3 | bigint |
+ c4 | bigint |
+View definition:
+ SELECT int8_tbl.q1, int8_tbl.q2, dfunc(int8_tbl.q1, int8_tbl.q2, int8_tbl.q1 > int8_tbl.q2 AS flag) AS c3, dfunc(int8_tbl.q1, int8_tbl.q1 < int8_tbl.q2 AS flag, int8_tbl.q2 AS b) AS c4
+ FROM int8_tbl;
+
+drop view dfview;
+drop function dfunc(anyelement, anyelement, bool);
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 486dd3f3fe0..843bc53e4e7 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -159,7 +159,7 @@ SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
1 | 1 | Joe
(1 row)
-CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
(fooid int, foosubid int, fooname text);
SELECT * FROM vw_getfoo;
fooid | foosubid | fooname
@@ -515,7 +515,13 @@ SELECT * FROM dup('xyz'::text);
xyz | {xyz,xyz}
(1 row)
--- equivalent specification
+-- fails, as we are attempting to rename first argument
+CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
+AS 'select $1, array[$1,$1]' LANGUAGE sql;
+ERROR: cannot change name of input parameter "f1"
+HINT: Use DROP FUNCTION first.
+DROP FUNCTION dup(anyelement);
+-- equivalent behavior, though different name exposed for input arg
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
SELECT dup(22);
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index c01871de007..2071ce63da7 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -1,5 +1,6 @@
-- Currently this tests polymorphic aggregates and indirectly does some
-- testing of polymorphic SQL functions. It ought to be extended.
+-- Tests for other features related to function-calling have snuck in, too.
-- Legend:
@@ -21,7 +22,7 @@
-- !> = not allowed
-- E = exists
-- NE = not-exists
---
+--
-- Possible states:
-- ----------------
-- B = (A || P || N)
@@ -69,7 +70,7 @@ CREATE FUNCTION ffnp(int[]) returns int[] as
'select $1' LANGUAGE SQL;
-- Try to cover all the possible states:
---
+--
-- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn
-- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp,
-- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
@@ -624,3 +625,123 @@ select dfunc('Hi');
drop function dfunc(int, int, int);
drop function dfunc(int, int);
drop function dfunc(text);
+
+--
+-- Tests for named- and mixed-notation function calling
+--
+
+create function dfunc(a int, b int, c int = 0, d int = 0)
+ returns table (a int, b int, c int, d int) as $$
+ select $1, $2, $3, $4;
+$$ language sql;
+
+select (dfunc(10,20,30)).*;
+select (dfunc(10 as a, 20 as b, 30 as c)).*;
+select * from dfunc(10 as a, 20 as b);
+select * from dfunc(10 as b, 20 as a);
+select * from dfunc(0); -- fail
+select * from dfunc(1,2);
+select * from dfunc(1,2,3 as c);
+select * from dfunc(1,2,3 as d);
+
+select * from dfunc(10 as x, 20 as b, 30 as x); -- fail, duplicate name
+select * from dfunc(10, 20 as b, 30); -- fail, named args must be last
+select * from dfunc(10 as x, 20 as b, 30 as c); -- fail, unknown param
+select * from dfunc(10, 10, 20 as a); -- fail, a overlaps positional parameter
+select * from dfunc(1,2 as c,3 as d); -- fail, no value for b
+
+drop function dfunc(int, int, int, int);
+
+-- test with different parameter types
+create function dfunc(a varchar, b numeric, c date = current_date)
+ returns table (a varchar, b numeric, c date) as $$
+ select $1, $2, $3;
+$$ language sql;
+
+select (dfunc('Hello World', 20, '2009-07-25'::date)).*;
+select * from dfunc('Hello World', 20, '2009-07-25'::date);
+select * from dfunc('2009-07-25'::date as c, 'Hello World' as a, 20 as b);
+select * from dfunc('Hello World', 20 as b, '2009-07-25'::date as c);
+select * from dfunc('Hello World', '2009-07-25'::date as c, 20 as b);
+select * from dfunc('Hello World', 20 as c, '2009-07-25'::date as b); -- fail
+
+drop function dfunc(varchar, numeric, date);
+
+-- test out parameters with named params
+create function dfunc(a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric)
+returns record as $$
+ select $1, $2;
+$$ language sql;
+
+select (dfunc()).*;
+select * from dfunc();
+select * from dfunc('Hello', 100);
+select * from dfunc('Hello' as a, 100 as c);
+select * from dfunc(100 as c, 'Hello' as a);
+select * from dfunc('Hello');
+select * from dfunc('Hello', 100 as c);
+select * from dfunc(100 as c);
+
+-- fail, can no longer change an input parameter's name
+create or replace function dfunc(a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric)
+returns record as $$
+ select $1, $2;
+$$ language sql;
+
+create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric)
+returns record as $$
+ select $1, $2;
+$$ language sql;
+
+drop function dfunc(varchar, numeric);
+
+--fail, named parameters are not unique
+create function testfoo(a int, a int) returns int as $$ select 1;$$ language sql;
+create function testfoo(int, out a int, out a int) returns int as $$ select 1;$$ language sql;
+create function testfoo(out a int, inout a int) returns int as $$ select 1;$$ language sql;
+create function testfoo(a int, inout a int) returns int as $$ select 1;$$ language sql;
+
+-- valid
+create function testfoo(a int, out a int) returns int as $$ select $1;$$ language sql;
+select testfoo(37);
+drop function testfoo(int);
+create function testfoo(a int) returns table(a int) as $$ select $1;$$ language sql;
+select * from testfoo(37);
+drop function testfoo(int);
+
+-- test polymorphic params and defaults
+create function dfunc(a anyelement, b anyelement = null, flag bool = true)
+returns anyelement as $$
+ select case when $3 then $1 else $2 end;
+$$ language sql;
+
+select dfunc(1,2);
+select dfunc('a'::text, 'b'); -- positional notation with default
+
+select dfunc(1 as a, 2 as b);
+select dfunc('a'::text as a, 'b' as b);
+select dfunc('a'::text as a, 'b' as b, false as flag); -- named notation
+
+select dfunc('b'::text as b, 'a' as a); -- named notation with default
+select dfunc('a'::text as a, true as flag); -- named notation with default
+select dfunc('a'::text as a, false as flag); -- named notation with default
+select dfunc('b'::text as b, 'a' as a, true as flag); -- named notation
+
+select dfunc('a'::text, 'b', false); -- full positional notation
+select dfunc('a'::text, 'b', false as flag); -- mixed notation
+select dfunc('a'::text, 'b', true); -- full positional notation
+select dfunc('a'::text, 'b', true as flag); -- mixed notation
+
+-- check reverse-listing of named-arg calls
+CREATE VIEW dfview AS
+ SELECT q1, q2,
+ dfunc(q1,q2, q1>q2 as flag) as c3,
+ dfunc(q1, q1<q2 as flag, q2 AS b) as c4
+ FROM int8_tbl;
+
+select * from dfview;
+
+\d dfview
+
+drop view dfview;
+drop function dfunc(anyelement, anyelement, bool);
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 3727a36aaff..172bbc73a9e 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -70,7 +70,7 @@ DROP VIEW vw_getfoo;
DROP FUNCTION getfoo(int);
CREATE FUNCTION getfoo(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
-CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
(fooid int, foosubid int, fooname text);
SELECT * FROM vw_getfoo;
@@ -251,7 +251,13 @@ SELECT dup('xyz'); -- fails
SELECT dup('xyz'::text);
SELECT * FROM dup('xyz'::text);
--- equivalent specification
+-- fails, as we are attempting to rename first argument
+CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
+AS 'select $1, array[$1,$1]' LANGUAGE sql;
+
+DROP FUNCTION dup(anyelement);
+
+-- equivalent behavior, though different name exposed for input arg
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
SELECT dup(22);