*** ./doc/src/sgml/xfunc.sgml.orig 2009-08-17 08:30:01.078081072 +0200
--- ./doc/src/sgml/xfunc.sgml 2009-08-18 08:04:12.807875233 +0200
***************
*** 1101,1106 ****
--- 1101,1294 ----
+
+ Positional and named notation
+
+
+ notation
+ functions
+
+
+
+ Functions with named parameters can be called by positional
+ or named notation. Named notation makes it
+ easier to recognize the signature of a function. This
+ applies especially to overloaded functions with larger argument lists. This is also closer to
+ some other SQL dialects which should make porting applications easier. The positional
+ notation calls the function with its argument values in the same order as defined in the function declaration.
+ Using named notation allows to
+ pass arguments by its name to a function, the positional order doesn't need to be preserved. Both
+ named and positional notation can be mixed
+ (mixed notation). However, positional notation is
+ only allowed from the left to the right in the argument list.
+ As soon as a named argument appears in the list, named
+ notation have to be used for all following arguments. Arguments with default values can be omitted and
+ don't have to be specified. The default values of such arguments will then be used instead. The following
+ example will illustrate the usage of all three kinds of notation.
+
+
+
+ CREATE OR REPLACE FUNCTION concat_lower_or_upper(a IN text, b IN text, uppercase boolean DEFAULT false)
+ RETURNS text
+ AS
+ $$
+ SELECT CASE
+ WHEN $3 THEN UPPER($1) || ' ' || UPPER($2)
+ ELSE LOWER($1) || ' ' || LOWER($2)
+ END;
+ $$
+ LANGUAGE SQL IMMUTABLE STRICT;
+
+ Function concat_lower_or_upper has two mandatory parameters: a and
+ b. Additionally there is one optional parameter uppercase which defaults to false.
+ This function can be called with positional, named or
+ mixed notation . See also for a more detailed explanation of calling
+ function with default values.
+
+
+
+ Using positional notation
+
+
+ function
+ positional notation
+
+
+
+ Positional notation is the traditional behavior how arguments are passed to functions in
+ PostgreSQL, for example:
+
+ SELECT concat_lower_or_upper('Hello', 'World');
+ concat_lower_or_upper
+ -----------------------
+ hello world
+ (1 row)
+
+ This calls the function concat_lower_or_upper with both mandatory arguments
+ a and b in positional notation
+ and omits all default arguments. The argument uppercase will get its
+ default value false assigned implicitely. Another example:
+
+ SELECT concat_lower_or_upper('Hello', 'World', true);
+ concat_lower_or_upper
+ -----------------------
+ HELLO WORLD
+ (1 row)
+
+ Now the function concat_lower_or_upper is called with all arguments in
+ positional notation. In this case the argument uppercase
+ will have true assigned explicitely and concat a and b
+ in uppercase.
+
+
+
+
+ Using named notation
+
+
+ function
+ named notation
+
+
+
+ Using named notation this time, the mandatory arguments a and b
+ are specified with the AS keyword.
+
+ SELECT concat_lower_or_upper('Hello' AS a, 'World' As b);
+ concat_lower_or_upper
+ -----------------------
+ hello world
+ (1 row)
+
+ Again, the default argument uppercase is omitted and set to false implicitly. Furthermore,
+ when using named notation the order of the arguments doesn't matter, for example:
+
+ SELECT concat_lower_or_upper('Hello' AS a, 'World' As b, true AS uppercase);
+ concat_lower_or_upper
+ -----------------------
+ HELLO WORLD
+ (1 row)
+
+ SELECT concat_lower_or_upper('Hello' AS a, true AS uppercase, 'World' AS b);
+ concat_lower_or_upper
+ -----------------------
+ HELLO WORLD
+ (1 row)
+
+ In the examples above, the named notation allows to specify the argument values out of
+ their original order as they would occur in positional notation. An important issue to consider when
+ using named notation is that you cannot use functions with variadic argument lists. However,
+ it is possible to refer to variadic functions with named variadic argument lists, if you want to pass the value as an
+ array (see also for a more detailed explanation on variadic functions
+ and how they are used). The following example will call a function with a variadic argument list by name and pass all
+ values as an ARRAY expression:
+
+ CREATE OR REPLACE FUNCTION variadic_function(uppercase boolean, txt VARIADIC text[])
+ RETURNS SETOF text
+ AS
+ $$
+ DECLARE
+ i integer;
+ BEGIN
+ FOR i IN 1..array_upper(txt, 1) LOOP
+ RETURN NEXT CASE WHEN uppercase
+ THEN UPPER(txt[i]) ELSE lower(txt[i])
+ END;
+ END LOOP;
+
+ RETURN;
+ END;
+ $$
+ LANGUAGE plpgsql;
+
+ SELECT variadic_function(ARRAY['Hello', 'World'] AS txt, false AS uppercase);
+ variadic_function
+ -------------------
+ hello
+ world
+ (2 rows)
+
+ However, a named variadic argument can only be called the way shown in the example above. The VARIADIC
+ keyword must not be specified and a variadic notation of all arguments is not supported. To use variadic argument lists
+ you must use positional notation instead.
+
+
+
+
+ Using mixed notation
+
+
+ function
+ mixed notation
+
+
+
+ The mixed notation combines positional and named
+ notation. However, as already mentioned, positional argument have to occur from left to right, named
+ arguments cannot precede positional arguments. This means that in mixed notation named
+ arguments cannot be defined before positional arguments.
+
+ SELECT concat_lower_or_upper('Hello', 'World' AS b, true AS uppercase);
+ concat_lower_or_upper
+ -----------------------
+ HELLO WORLD
+ (1 row)
+
+ The first query above is correct, since the argument a is defined in
+ positional notation before any named arguments. However, the following example
+ isn't allowed to work, since a precedes the named argument b.
+
+ SELECT concat_lower_or_upper('World' AS b, 'Hello', true AS uppercase);
+ ERROR: expected named argument
+ LINE 1: SELECT concat_lower_or_upper('World' AS b, 'Hello', true AS ...
+ ^
+ HINT: You can't put positional arguments after named arguments.
+
+
+
+
+
Function Volatility Categories
*** ./src/backend/catalog/namespace.c.orig 2009-08-17 21:21:36.609249654 +0200
--- ./src/backend/catalog/namespace.c 2009-08-22 22:53:22.929256225 +0200
***************
*** 36,41 ****
--- 36,42 ----
#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,193 ****
--- 189,198 ----
static void RemoveTempRelations(Oid tempNamespaceId);
static void RemoveTempRelationsCallback(int code, Datum arg);
static void NamespaceCallback(Datum arg, int cacheid, ItemPointer tuplePtr);
+ static bool VerifyCandidateNamedNotation(HeapTuple proctup, int pronargs, int nargs, List *argnames,
+ bool expand_defaults, int pronargdefaults,
+ bool *use_defaults, int **proargidxs);
+
/* These don't really need to appear in any header file */
Datum pg_table_is_visible(PG_FUNCTION_ARGS);
***************
*** 604,610 ****
* such an entry it should react as though the call were ambiguous.
*/
FuncCandidateList
! FuncnameGetCandidates(List *names, int nargs,
bool expand_variadic, bool expand_defaults)
{
FuncCandidateList resultList = NULL;
--- 609,615 ----
* such an entry it should react as though the call were ambiguous.
*/
FuncCandidateList
! FuncnameGetCandidates(List *names, int nargs, List *argnames,
bool expand_variadic, bool expand_defaults)
{
FuncCandidateList resultList = NULL;
***************
*** 646,688 ****
int effective_nargs;
int pathpos = 0;
bool variadic;
! bool use_defaults;
Oid va_elem_type;
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))
{
--- 651,716 ----
int effective_nargs;
int pathpos = 0;
bool variadic;
! bool use_defaults = false; /* be compiler quiet */
Oid va_elem_type;
FuncCandidateList newResult;
+ int *proargidxs = NULL;
! /* Try to attach names, when mixed or named notation is used. */
! if (argnames != NIL)
{
! /*
! * Mixed or named notation
! */
! if (!VerifyCandidateNamedNotation(proctup, pronargs, nargs,
! argnames,
! expand_defaults,
! procform->pronargdefaults,
! &use_defaults,
! &proargidxs))
! continue;
!
! va_elem_type = InvalidOid;
! variadic = false;
}
else
{
! /*
! * 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;
}
if (OidIsValid(namespaceId))
{
***************
*** 722,727 ****
--- 750,762 ----
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = effective_nargs;
+
+ /*
+ * Wait with apply proargidxs on args. Detection ambigouos needs
+ * consistent args (based on proargs). Store proargidxs for later
+ * use.
+ */
+ newResult->proargidxs = proargidxs;
memcpy(newResult->args, procform->proargtypes.values,
pronargs * sizeof(Oid));
if (variadic)
***************
*** 886,891 ****
--- 921,1052 ----
}
/*
+ * VerifyCandidateNameNotation
+ * Given a pg_proc heap tuple and its list of named arguments,
+ * verify a possible notation candidate (these are named,
+ * positional or mixed notation currently), which matches
+ * a possible function signature candidate. Returns true if the
+ * argument list can be verified against the given function,
+ * otherwise false is returned.
+ */
+ static bool
+ VerifyCandidateNamedNotation(HeapTuple proctup, int pronargs, int nargs, List *argnames,
+ bool expand_defaults, int pronargdefaults,
+ bool *use_defaults, int **proargidxs)
+ {
+ Datum proargnames;
+ Oid *p_argtypes;
+ char **p_argnames;
+ char *p_argmodes;
+ bool isnull;
+ int pronallargs;
+ int i;
+ int pp; /* proargs position */
+ int ap; /* args position */
+ ListCell *lc;
+ bool argfilling[FUNC_MAX_ARGS]; /* function parameters occupation */
+
+ /* used for assertion only */
+ #ifdef USE_ASSERT_CHECKING
+ CallNotationType notation = CALL_NOTATION_POSITIONAL;
+ #endif
+
+ Assert(argnames != NIL);
+
+ /* Ignore if not enough default expressions */
+ if (nargs + pronargdefaults < pronargs)
+ return false;
+
+ /* Ignore if defaults are requested but not expanded */
+ if (nargs < pronargs && !expand_defaults)
+ return false;
+
+ /* check proargnames */
+ proargnames = SysCacheGetAttr(PROCOID, proctup,
+ Anum_pg_proc_proargnames,
+ &isnull);
+ if (isnull)
+ return false;
+
+ pronallargs = get_func_arg_info(proctup, &p_argtypes, &p_argnames, &p_argmodes);
+ Assert(p_argnames != NULL);
+
+ /*
+ * A number less or equal nargs means explicit arguments,
+ */
+ *proargidxs = palloc(nargs * sizeof(int));
+ for (i = 0; i < pronargs; i++)
+ argfilling[i] = false;
+
+ ap = 0;
+ foreach(lc, argnames)
+ {
+ char *argname = (char *) lfirst(lc);
+ bool found;
+
+ if (argname != NULL)
+ {
+ pp = 0;
+ found = false;
+ for (i = 0; i < pronallargs; i++)
+ {
+ /* skip all out params */
+ 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)
+ {
+ /* protect us against duplicated entries from bad written mixed notation */
+ if (argfilling[pp])
+ return false;
+
+ found = true;
+ argfilling[pp] = true;
+ (*proargidxs)[ap] = pp;
+ break;
+ }
+ /* increase only for IN and INOUT args */
+ pp++;
+ }
+ /* any name isn't in proargnames, abort */
+ if (!found)
+ return false;
+
+ #ifdef USE_ASSERT_CHECKING
+ notation = CALL_NOTATION_NAMED;
+ #endif
+ }
+ else
+ {
+ Assert(notation == CALL_NOTATION_POSITIONAL);
+
+ /* positional parameter */
+ argfilling[ap] = true;
+ (*proargidxs)[ap] = ap;
+ }
+ ap++;
+ }
+
+ Assert(notation == CALL_NOTATION_NAMED);
+
+ /* Check for default arguments ? */
+ if (nargs < pronargs)
+ {
+ int first_arg_with_default = pronargs - pronargdefaults;
+
+ for (i = 0; i < pronargs; i++)
+ {
+ /* When there's a param still missing and no default is available, exit */
+ if (!argfilling[i] && i < first_arg_with_default)
+ return false;
+ }
+ *use_defaults = true;
+ }
+
+ 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,938 ****
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! nargs, false, false);
for (; clist; clist = clist->next)
{
--- 1093,1099 ----
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! nargs, NIL, false, false);
for (; clist; clist = clist->next)
{
*** ./src/backend/catalog/pg_aggregate.c.orig 2009-08-18 07:42:31.578875233 +0200
--- ./src/backend/catalog/pg_aggregate.c 2009-08-18 07:42:51.344877364 +0200
***************
*** 321,327 ****
* 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,
&fnOid, rettype, &retset, &nvargs,
&true_oid_array, NULL);
--- 321,327 ----
* function's return value. it also returns the true argument types to
* the function.
*/
! fdresult = func_get_detail(fnName, NIL, NIL, nargs, input_types, false, false,
&fnOid, rettype, &retset, &nvargs,
&true_oid_array, NULL);
*** ./src/backend/catalog/pg_proc.c.orig 2009-08-16 14:28:41.057769526 +0200
--- ./src/backend/catalog/pg_proc.c 2009-08-24 18:58:34.228620912 +0200
***************
*** 101,106 ****
--- 101,108 ----
bool is_update;
ObjectAddress myself,
referenced;
+ bool isnull;
+ Datum prooldargnames;
int i;
/*
***************
*** 403,409 ****
if (oldproc->pronargdefaults != 0)
{
Datum proargdefaults;
- bool isnull;
List *oldDefaults;
ListCell *oldlc;
ListCell *newlc;
--- 405,410 ----
***************
*** 442,447 ****
--- 443,524 ----
newlc = lnext(newlc);
}
}
+
+ /*
+ * if there are named parameters, check names equality. Any change can break
+ * exiting calls (when named parameters are used). Only IN and INOUT parameters are
+ * checked.
+ */
+ prooldargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+ Anum_pg_proc_proargnames,
+ &isnull);
+ if (!isnull)
+ {
+ Oid *p_oldargtypes;
+ char **p_oldargnames;
+ char *p_oldargmodes;
+ int pronoldallargs;
+ char *p_modes = NULL;
+ char **p_names = NULL;
+ int j;
+
+ pronoldallargs = get_func_arg_info(oldtup, &p_oldargtypes,
+ &p_oldargnames,
+ &p_oldargmodes);
+ Assert(PointerIsValid(p_oldargnames));
+
+ if (parameterModes != PointerGetDatum(NULL))
+ {
+ ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
+
+ p_modes = (char *) ARR_DATA_PTR(modesArray);
+ }
+
+ if (parameterNames != PointerGetDatum(NULL))
+ {
+ Datum *elems;
+ int nelems;
+
+ deconstruct_array(DatumGetArrayTypeP(parameterNames),
+ TEXTOID, -1, false, 'i',
+ &elems, NULL, &nelems);
+ Assert(nelems == allParamCount);
+ p_names = (char **) palloc(sizeof(char *) * nelems);
+ for (i = 0; i < nelems; i++)
+ p_names[i] = TextDatumGetCString(elems[i]);
+ }
+
+ /* compare names of IN and INOUT parameters */
+ for (i = 0, j = 0; i < pronoldallargs; i++)
+ {
+ /* skip old output arguments */
+ if (p_oldargmodes != NULL && (p_oldargmodes[i] == PROARGMODE_OUT ||
+ p_oldargmodes[i] == PROARGMODE_TABLE))
+ continue;
+ /* find first new input arguments */
+ for ( ;j < allParamCount; j++)
+ if (p_modes == NULL || (p_modes != NULL && (p_modes[j] == PROARGMODE_IN ||
+ p_modes[j] == PROARGMODE_INOUT ||
+ p_modes[j] == PROARGMODE_VARIADIC)))
+ break;
+
+ /* j should to be valid index */
+ Assert(j < allParamCount);
+ if (p_oldargnames != NULL && *p_oldargnames[i] != '\0')
+ {
+ bool valid;
+ /* when original argname is same as new name, then name is valided */
+ valid = p_names != NULL && *p_names[j] != '\0'
+ && strcmp(p_oldargnames[i], p_names[j]) == 0;
+ if (!valid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("cannot change name of existing input parameter"),
+ errhint("Use DROP FUNCTION first.")));
+ }
+ j++;
+ }
+ }
/* Can't change aggregate or window-function status, either */
if (oldproc->proisagg != isAgg)
*** ./src/backend/nodes/copyfuncs.c.orig 2009-08-16 18:06:26.333081325 +0200
--- ./src/backend/nodes/copyfuncs.c 2009-08-19 21:24:57.823629391 +0200
***************
*** 1013,1018 ****
--- 1013,1019 ----
COPY_SCALAR_FIELD(funcresulttype);
COPY_SCALAR_FIELD(funcretset);
COPY_SCALAR_FIELD(funcformat);
+ COPY_SCALAR_FIELD(notation);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
***************
*** 1020,1025 ****
--- 1021,1042 ----
}
/*
+ * _copyNamedArgExpr *
+ */
+ static NamedArgExpr *
+ _copyNamedArgExpr(NamedArgExpr *from)
+ {
+ NamedArgExpr *newnode = makeNode(NamedArgExpr);
+
+ COPY_STRING_FIELD(name);
+ COPY_NODE_FIELD(arg);
+ COPY_SCALAR_FIELD(position);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+ }
+
+ /*
* _copyOpExpr
*/
static OpExpr *
***************
*** 3564,3569 ****
--- 3581,3589 ----
case T_FuncExpr:
retval = _copyFuncExpr(from);
break;
+ case T_NamedArgExpr:
+ retval = _copyNamedArgExpr(from);
+ break;
case T_OpExpr:
retval = _copyOpExpr(from);
break;
*** ./src/backend/nodes/equalfuncs.c.orig 2009-08-16 18:06:34.431092306 +0200
--- ./src/backend/nodes/equalfuncs.c 2009-08-19 21:25:28.061629432 +0200
***************
*** 235,240 ****
--- 235,241 ----
b->funcformat != COERCE_DONTCARE)
return false;
+ COMPARE_SCALAR_FIELD(notation);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
***************
*** 242,247 ****
--- 243,259 ----
}
static bool
+ _equalNamedArgExpr(NamedArgExpr *a, NamedArgExpr *b)
+ {
+ COMPARE_STRING_FIELD(name);
+ COMPARE_NODE_FIELD(arg);
+ COMPARE_SCALAR_FIELD(position);
+ COMPARE_LOCATION_FIELD(location);
+
+ return true;
+ }
+
+ static bool
_equalOpExpr(OpExpr *a, OpExpr *b)
{
COMPARE_SCALAR_FIELD(opno);
***************
*** 2356,2361 ****
--- 2368,2376 ----
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
break;
+ case T_NamedArgExpr:
+ retval = _equalNamedArgExpr(a, b);
+ break;
case T_OpExpr:
retval = _equalOpExpr(a, b);
break;
*** ./src/backend/nodes/makefuncs.c.orig 2009-08-16 21:59:50.167081941 +0200
--- ./src/backend/nodes/makefuncs.c 2009-08-16 22:00:59.905077291 +0200
***************
*** 342,347 ****
--- 342,348 ----
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = false; /* only allowed case here */
funcexpr->funcformat = fformat;
+ funcexpr->notation = CALL_NOTATION_POSITIONAL;
funcexpr->args = args;
funcexpr->location = -1;
*** ./src/backend/nodes/nodeFuncs.c.orig 2009-08-16 23:40:57.031078716 +0200
--- ./src/backend/nodes/nodeFuncs.c 2009-08-19 17:13:09.984628024 +0200
***************
*** 69,74 ****
--- 69,77 ----
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,264 ****
--- 262,269 ----
return coercedTypmod;
}
break;
+ case T_NamedArgExpr:
+ return exprTypmod((Node *) ((NamedArgExpr *) expr)->arg);
case T_SubLink:
{
SubLink *sublink = (SubLink *) expr;
***************
*** 676,681 ****
--- 681,689 ----
exprLocation((Node *) fexpr->args));
}
break;
+ case T_NamedArgExpr:
+ loc = ((NamedArgExpr *) expr)->location;
+ break;
case T_OpExpr:
case T_DistinctExpr: /* struct-equivalent to OpExpr */
case T_NullIfExpr: /* struct-equivalent to OpExpr */
***************
*** 1345,1355 ****
--- 1353,1366 ----
break;
case T_PlaceHolderInfo:
return walker(((PlaceHolderInfo *) node)->ph_var, context);
+ case T_NamedArgExpr:
+ return walker(((NamedArgExpr *) node)->arg, context);
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
break;
}
+
return false;
}
***************
*** 2019,2024 ****
--- 2030,2045 ----
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;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
*** ./src/backend/nodes/outfuncs.c.orig 2009-08-16 18:06:45.800078810 +0200
--- ./src/backend/nodes/outfuncs.c 2009-08-19 21:27:08.403628654 +0200
***************
*** 871,881 ****
--- 871,893 ----
WRITE_OID_FIELD(funcresulttype);
WRITE_BOOL_FIELD(funcretset);
WRITE_ENUM_FIELD(funcformat, CoercionForm);
+ WRITE_ENUM_FIELD(notation, CallNotationType);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
}
static void
+ _outNamedArgExpr(StringInfo str, NamedArgExpr *node)
+ {
+ WRITE_NODE_TYPE("NAMEDARGEXPR");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_STRING_FIELD(name);
+ WRITE_INT_FIELD(position);
+ WRITE_LOCATION_FIELD(location);
+ }
+
+ static void
_outOpExpr(StringInfo str, OpExpr *node)
{
WRITE_NODE_TYPE("OPEXPR");
***************
*** 2504,2509 ****
--- 2516,2524 ----
case T_FuncExpr:
_outFuncExpr(str, obj);
break;
+ case T_NamedArgExpr:
+ _outNamedArgExpr(str, obj);
+ break;
case T_OpExpr:
_outOpExpr(str, obj);
break;
*** ./src/backend/nodes/readfuncs.c.orig 2009-08-16 18:06:57.124083014 +0200
--- ./src/backend/nodes/readfuncs.c 2009-08-19 21:51:23.547629891 +0200
***************
*** 519,524 ****
--- 519,525 ----
READ_OID_FIELD(funcresulttype);
READ_BOOL_FIELD(funcretset);
READ_ENUM_FIELD(funcformat, CoercionForm);
+ READ_ENUM_FIELD(notation, CallNotationType);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
***************
*** 526,531 ****
--- 527,548 ----
}
/*
+ * _readNamedArgExpr
+ */
+ static NamedArgExpr *
+ _readNamedArgExpr(void)
+ {
+ READ_LOCALS(NamedArgExpr);
+
+ READ_NODE_FIELD(arg);
+ READ_STRING_FIELD(name);
+ READ_INT_FIELD(position);
+ READ_LOCATION_FIELD(location);
+
+ READ_DONE();
+ }
+
+ /*
* _readOpExpr
*/
static OpExpr *
***************
*** 1207,1212 ****
--- 1224,1231 ----
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))
*** ./src/backend/optimizer/util/clauses.c.orig 2009-08-19 16:59:03.584632290 +0200
--- ./src/backend/optimizer/util/clauses.c 2009-08-24 06:55:34.045006379 +0200
***************
*** 94,105 ****
bool *haveNull, bool *forceFalse);
static Expr *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
! Oid result_type, int32 result_typmod, List **args,
bool allow_inline,
eval_const_expressions_context *context);
static List *add_function_defaults(List *args, Oid result_type,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
HeapTuple func_tuple,
--- 94,109 ----
bool *haveNull, bool *forceFalse);
static Expr *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
! Oid result_type, int32 result_typmod,
! CallNotationType notation,
! List **args,
bool allow_inline,
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 *reorder_arguments(List *args, Oid result_type, HeapTuple func_tuple,
+ eval_const_expressions_context *context);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
HeapTuple func_tuple,
***************
*** 2133,2138 ****
--- 2137,2143 ----
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
+ expr->notation,
&args,
true, context);
if (simple) /* successfully simplified it */
***************
*** 2180,2185 ****
--- 2185,2191 ----
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
+ CALL_NOTATION_POSITIONAL,
&args,
true, context);
if (simple) /* successfully simplified it */
***************
*** 2273,2278 ****
--- 2279,2285 ----
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
+ CALL_NOTATION_POSITIONAL,
&args,
false, context);
if (simple) /* successfully simplified it */
***************
*** 2465,2470 ****
--- 2472,2478 ----
simple = simplify_function(outfunc,
CSTRINGOID, -1,
+ CALL_NOTATION_POSITIONAL,
&args,
true, context);
if (simple) /* successfully simplified output fn */
***************
*** 2483,2488 ****
--- 2491,2497 ----
simple = simplify_function(infunc,
expr->resulttype, -1,
+ CALL_NOTATION_POSITIONAL,
&args,
true, context);
if (simple) /* successfully simplified input fn */
***************
*** 3249,3254 ****
--- 3258,3264 ----
*/
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
+ CallNotationType notation,
List **args,
bool allow_inline,
eval_const_expressions_context *context)
***************
*** 3270,3277 ****
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))
*args = add_function_defaults(*args, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
--- 3280,3290 ----
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
+ /* When named notation is used, reorder arguments and if we need, then add defaults */
+ if (notation == CALL_NOTATION_NAMED)
+ *args = reorder_arguments(*args, result_type, func_tuple, context);
/* While we have the tuple, check if we need to add defaults */
! 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,3293 ****
}
/*
! * 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
--- 3300,3443 ----
}
/*
! * This function change order of any arg in arglist. When some arguments missing,
! * then it use defaults.
! */
! static List *
! reorder_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 nargs;
! int firstdefpos = 0; /* be compiler quiet */
! List *defaults;
! List *newargs;
! ListCell *lc;
! Oid rettype;
! Oid actual_arg_types[FUNC_MAX_ARGS];
! Oid declared_arg_types[FUNC_MAX_ARGS];
! int i;
! Bitmapset *defargidxs;
!
! nargs = list_length(args);
!
! /* Load defaults, when it's necessary */
! if (nargs < funcform->pronargs)
! {
! Datum proargdefaults;
! bool isnull;
! char *str;
!
! 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));
!
! firstdefpos = funcform->pronargs - funcform->pronargdefaults;
! pfree(str);
! }
! else
! {
! /* There are no defaults */
! defaults = NIL;
! }
!
! i = 0;
! newargs = NIL;
! defargidxs = NULL;
! foreach (lc, args)
! {
! Node *node = (Node *) lfirst(lc);
!
! /* process first n positional arguments */
! if (!IsA(node, NamedArgExpr))
! {
! newargs = lappend(newargs, node);
! actual_arg_types[i] = exprType(node);
! i++;
! }
! else
! {
! ListCell *l;
! bool found;
!
! /* process other necessary arguments */
! for ( ; i < funcform->pronargs; i++)
! {
! found = false;
! for_each_cell (l, lc)
! {
! NamedArgExpr *namedarg = (NamedArgExpr *) lfirst(l);
!
! Assert(IsA(namedarg, NamedArgExpr));
!
! if (namedarg->position == i)
! {
! newargs = lappend(newargs, namedarg->arg);
! actual_arg_types[i] = exprType((Node *) namedarg->arg);
! found = true;
! break;
! }
! }
!
! /* When there are no argument for specified position, use default */
! if (!found)
! {
! Node *defexpr;
!
! Assert(defaults != NIL);
! defexpr = (Node *) list_nth(defaults, i - firstdefpos);
! newargs = lappend(newargs, defexpr);
! actual_arg_types[i] = exprType(defexpr);
! defargidxs = bms_add_member(defargidxs, i);
! nargs++;
! }
! }
! break;
! }
! }
!
! Assert(list_length(newargs) == funcform->pronargs);
! Assert(nargs == funcform->pronargs);
!
! memcpy(declared_arg_types, funcform->proargtypes.values,
! funcform->pronargs * sizeof(Oid));
! rettype = enforce_generic_type_consistency(actual_arg_types,
! declared_arg_types,
! nargs,
! funcform->prorettype,
! false);
! /* let's just check we got the same answer as the parser did ... */
! if (rettype != result_type)
! elog(ERROR, "function's resolved result type changed during planning");
!
! /* perform any necessary typecasting of arguments */
! make_fn_arguments(NULL, newargs, 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.
! */
! i = 0;
! foreach(lc, newargs)
! {
! if (bms_is_member(i, defargidxs))
! lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
! context);
! }
! bms_free(defargidxs);
!
! return newargs;
! }
!
! /*
! * add_function_defaults_positional_notation: 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
*** ./src/backend/parser/gram.y.orig 2009-08-16 17:48:52.118082051 +0200
--- ./src/backend/parser/gram.y 2009-08-24 19:43:14.009621288 +0200
***************
*** 137,142 ****
--- 137,145 ----
static List *mergeTableFuncParameters(List *func_args, List *columns);
static TypeName *TableFuncTypeName(List *columns);
+ static List *AppendFuncParameter(List *list, FunctionParameter *p,
+ int location, base_yyscan_t yyscanner);
+
%}
%pure-parser
***************
*** 348,353 ****
--- 351,358 ----
%type 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 func_arg_list
+ %type func_arg_expr
%type row type_list array_expr_list
%type case_expr case_arg when_clause case_default
%type when_clause_list
***************
*** 4693,4699 ****
func_args_list:
func_arg { $$ = list_make1($1); }
! | func_args_list ',' func_arg { $$ = lappend($1, $3); }
;
/*
--- 4698,4704 ----
func_args_list:
func_arg { $$ = list_make1($1); }
! | func_args_list ',' func_arg { $$ = AppendFuncParameter($1, $3, @3, yyscanner); }
;
/*
***************
*** 4707,4714 ****
func_args_with_defaults_list:
func_arg_with_default { $$ = list_make1($1); }
! | func_args_with_defaults_list ',' func_arg_with_default
! { $$ = lappend($1, $3); }
;
/*
--- 4712,4718 ----
func_args_with_defaults_list:
func_arg_with_default { $$ = list_make1($1); }
! | func_args_with_defaults_list ',' func_arg_with_default { $$ = AppendFuncParameter($1, $3, @3, yyscanner); }
;
/*
***************
*** 8854,8860 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8858,8864 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
***************
*** 8866,8872 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' VARIADIC a_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8870,8876 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' VARIADIC func_arg_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
***************
*** 8878,8884 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8882,8888 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
***************
*** 8890,8896 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' ALL expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8894,8900 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' ALL func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
***************
*** 8906,8912 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' DISTINCT expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8910,8916 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' DISTINCT func_arg_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
***************
*** 9629,9634 ****
--- 9633,9665 ----
}
;
+ /* used for named notation support
+ */
+ 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 *fa = makeNode(NamedArgExpr);
+ fa->arg = (Expr *) $1;
+ fa->name = $3;
+ fa->location = @1;
+ $$ = (Node *) fa;
+ }
+ ;
+
type_list: Typename { $$ = list_make1($1); }
| type_list ',' Typename { $$ = lappend($1, $3); }
;
***************
*** 10095,10104 ****
t->location = @1;
$$ = makeStringConstCast($2, @2, t);
}
! | func_name '(' expr_list ')' Sconst
{
/* generic syntax with a type modifier */
TypeName *t = makeTypeNameFromNameList($1);
t->typmods = $3;
t->location = @1;
$$ = makeStringConstCast($5, @5, t);
--- 10126,10147 ----
t->location = @1;
$$ = makeStringConstCast($2, @2, t);
}
! | func_name '(' func_arg_list ')' Sconst
{
/* generic syntax with a type modifier */
TypeName *t = makeTypeNameFromNameList($1);
+ ListCell *lc;
+
+ /* Don't allow NamedArgExpr in this context */
+ foreach(lc, $3)
+ {
+ if (IsA((Node *) lfirst(lc), NamedArgExpr))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("type modifier has name"),
+ errhint("Don't use keyword \"AS\" in this context"),
+ parser_errposition(exprLocation((Node *) lfirst(lc)))));
+ }
t->typmods = $3;
t->location = @1;
$$ = makeStringConstCast($5, @5, t);
***************
*** 11198,11203 ****
--- 11241,11286 ----
}
/*
+ * Append function parameter to function's parameter list. Reject
+ * parameters with uniqueless param_name with same proargmode.
+ * It's enough for sql, plperl, plpython language, but not for
+ * plpgsql.
+ *
+ * Function f1(IN a int, IN b int, OUT a int, OUT b int)
+ * is same as f1(INOUT a int, INOUT b int) and is valid for
+ * languages without variables for OUT parameters. Full check
+ * must be done by language validation handlers, when it's
+ * necessary.
+ */
+ static List *
+ AppendFuncParameter(List *list, FunctionParameter *p, int location, base_yyscan_t yyscanner)
+ {
+ ListCell *lc;
+
+ if (p->name != NULL)
+ foreach(lc, list)
+ {
+ FunctionParameter *p2 = (FunctionParameter *) lfirst(lc);
+
+ if (p->mode == FUNC_PARAM_IN && (p2->mode == FUNC_PARAM_OUT ||
+ p2->mode == FUNC_PARAM_TABLE))
+ continue;
+
+ if (p->mode == FUNC_PARAM_OUT && (p2->mode == FUNC_PARAM_IN ||
+ p2->mode == FUNC_PARAM_VARIADIC))
+ continue;
+
+ if (p2->name != NULL && strcmp(p->name, p2->name) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("parameter has not unique name"),
+ parser_errposition(location)));
+ }
+
+ return lappend(list, p);
+ }
+
+ /*
* Must undefine base_yylex before including scan.c, since we want it
* to create the function base_yylex not filtered_base_yylex.
*/
*** ./src/backend/parser/parse_expr.c.orig 2009-08-16 23:06:56.695077881 +0200
--- ./src/backend/parser/parse_expr.c 2009-08-17 07:22:29.572079963 +0200
***************
*** 255,260 ****
--- 255,269 ----
case T_XmlSerialize:
result = transformXmlSerialize(pstate, (XmlSerialize *) expr);
break;
+
+ case T_NamedArgExpr:
+ {
+ NamedArgExpr *n = (NamedArgExpr *) expr;
+
+ n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg);
+ result = expr;
+ break;
+ }
case T_NullTest:
{
*** ./src/backend/parser/parse_func.c.orig 2009-08-16 23:07:39.330080505 +0200
--- ./src/backend/parser/parse_func.c 2009-08-24 07:11:26.023009544 +0200
***************
*** 75,80 ****
--- 75,82 ----
bool retset;
int nvargs;
FuncDetailCode fdresult;
+ CallNotationType notation = CALL_NOTATION_POSITIONAL; /* keep compiler quiet */
+ List *fargnames;
/*
* Most of the rest of the parser just assumes that functions do not have
***************
*** 123,128 ****
--- 125,188 ----
Assert(first_arg != NULL);
}
+ /* Identify call notation, check it and param name uniqueness. */
+ fargnames = NIL;
+ foreach (l, fargs)
+ {
+ Node *arg = lfirst(l);
+ ListCell *lc;
+
+ if (IsA(arg, NamedArgExpr))
+ {
+ NamedArgExpr *n = (NamedArgExpr *) arg;
+
+ /* n->name is valid pointer to non empty string */
+ Assert(n->name != NULL);
+ Assert(*n->name != '\0');
+
+ /*
+ * There are not difference in processing between
+ * mixed and named notation - mixed notation is processed
+ * like named notation, so it is marked as named notation
+ * too.
+ */
+ notation = CALL_NOTATION_NAMED;
+
+ /* Check duplicates */
+ for_each_cell(lc, lnext(l))
+ {
+ if (IsA(lfirst(lc), NamedArgExpr))
+ {
+ char *next_name = ((NamedArgExpr *) lfirst(lc))->name;
+
+ Assert(next_name != NULL);
+ if (strcmp(n->name, next_name) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("named argument has not unique name"),
+ errhint("Verify your named parameters for ambiguous argument names."),
+ parser_errposition(pstate, exprLocation((Node *) lfirst(lc)))));
+ }
+ }
+ fargnames = lappend(fargnames, n->name);
+ }
+ else
+ {
+ if (notation != CALL_NOTATION_POSITIONAL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("expected named argument"),
+ errhint("You can't put positional arguments after named arguments."),
+ parser_errposition(pstate, exprLocation(arg))));
+
+ fargnames = lappend(fargnames, NULL);
+ }
+ }
+
+ /* forgot argnames list of empty strings when positional notation */
+ if (notation == CALL_NOTATION_POSITIONAL)
+ fargnames = NIL;
+
/*
* Check for column projection: if function has one argument, and that
* argument is of complex type, and function name is not qualified, then
***************
*** 130,136 ****
* wasn't any aggregate or variadic decoration.
*/
if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
! !func_variadic && list_length(funcname) == 1)
{
Oid argtype = actual_arg_types[0];
--- 190,196 ----
* wasn't any aggregate or variadic decoration.
*/
if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
! !func_variadic && list_length(funcname) == 1 && notation == CALL_NOTATION_POSITIONAL)
{
Oid argtype = actual_arg_types[0];
***************
*** 161,167 ****
* 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.
*/
! fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
!func_variadic, true,
&funcid, &rettype, &retset, &nvargs,
&declared_arg_types, &argdefaults);
--- 221,227 ----
* 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.
*/
! fdresult = func_get_detail(funcname, fargs, fargnames, nargs, actual_arg_types,
!func_variadic, true,
&funcid, &rettype, &retset, &nvargs,
&declared_arg_types, &argdefaults);
***************
*** 320,325 ****
--- 380,386 ----
funcexpr->funcretset = retset;
funcexpr->funcformat = COERCE_EXPLICIT_CALL;
funcexpr->args = fargs;
+ funcexpr->notation = notation;
funcexpr->location = location;
retval = (Node *) funcexpr;
***************
*** 809,814 ****
--- 870,876 ----
FuncDetailCode
func_get_detail(List *funcname,
List *fargs,
+ List *fargnames,
int nargs,
Oid *argtypes,
bool expand_variadic,
***************
*** 833,841 ****
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
! raw_candidates = FuncnameGetCandidates(funcname, nargs,
expand_variadic, expand_defaults);
/*
* Quickly check if there is an exact match to the input datatypes (there
* can be only one)
--- 895,922 ----
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
! raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
expand_variadic, expand_defaults);
+ /* Adjust args to order specified by arg names */
+ if (fargnames != NIL)
+ {
+ for (best_candidate = raw_candidates;
+ best_candidate != NULL;
+ best_candidate = best_candidate->next)
+ {
+ Oid proargtypes[FUNC_MAX_ARGS];
+ int *proargidxs = best_candidate->proargidxs;
+ int i;
+
+ Assert(proargidxs != NULL);
+
+ memcpy(proargtypes, best_candidate->args, best_candidate->nargs * sizeof(Oid));
+ for (i = 0; i < nargs; i++)
+ best_candidate->args[i] = proargtypes[proargidxs[i]];
+ }
+ }
+
/*
* Quickly check if there is an exact match to the input datatypes (there
* can be only one)
***************
*** 978,983 ****
--- 1059,1078 ----
*nvargs = best_candidate->nvargs;
*true_typeids = best_candidate->args;
+ /* Append positions to NamedArgExpr nodes */
+ if (best_candidate->proargidxs != NULL)
+ {
+ int i = 0;
+ ListCell *lc;
+
+ foreach (lc, fargs)
+ {
+ if (IsA(lfirst(lc), NamedArgExpr))
+ ((NamedArgExpr *) lfirst(lc))->position = best_candidate->proargidxs[i];
+ i++;
+ }
+ }
+
ftup = SearchSysCache(PROCOID,
ObjectIdGetDatum(best_candidate->oid),
0, 0, 0);
***************
*** 1010,1020 ****
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;
--- 1105,1166 ----
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
!
! /* Delete any unused defaults from returned list */
! if (best_candidate->proargidxs != NULL)
! {
! /* Defaults for named notation */
! Bitmapset *argidxs;
! ListCell *lc;
! List *ndefaults;
! int i;
! int firstdefpos;
! int ndargs;
!
! argidxs = NULL;
! i = 0;
! foreach(lc, fargs)
! {
! Node *node = lfirst(lc);
!
! if (!IsA((Node *) node, NamedArgExpr))
! {
! argidxs = bms_add_member(argidxs, i);
! i++;
! }
! else
! argidxs = bms_add_member(argidxs, ((NamedArgExpr *) node)->position);
! }
!
! /*
! * Actually only first nargs field of best_candidate->args is valid,
! * Now, we have to refresh last ndargs items.
! */
! ndargs = 0;
! ndefaults = NIL;
! firstdefpos = pform->pronargs - pform->pronargdefaults;
! for (i = 0; i < pform->pronargs; i++)
! {
! if (!bms_is_member(i, argidxs))
! {
! ndefaults = lappend(ndefaults, list_nth(defaults, i - firstdefpos));
! (*true_typeids)[nargs + ndargs++] = pform->proargtypes.values[i];
! }
! }
!
! Assert(ndargs == best_candidate->ndargs);
!
! bms_free(argidxs);
! *argdefaults = ndefaults;
! }
! else
! {
! /* Defaults for positional notation */
! ndelete = list_length(defaults) - best_candidate->ndargs;
! while (ndelete-- > 0)
! defaults = list_delete_first(defaults);
! *argdefaults = defaults;
! }
}
else
*argdefaults = NIL;
***************
*** 1060,1075 ****
/* 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);
! }
i++;
! }
}
/*
--- 1206,1251 ----
/* types don't match? then force coercion using a function call... */
if (actual_arg_types[i] != declared_arg_types[i])
{
! Node *node = (Node *) lfirst(current_fargs);
! Node *result;
! NamedArgExpr *namedarg = NULL;
!
! /*
! * Extract expr node from NamedArgExpr - don't add new complexity
! * to coerce_type func - don't enhance coerce_type for envelope
! * nodes.
! */
! if (IsA(node, NamedArgExpr))
! {
! namedarg = (NamedArgExpr *) node;
! node = (Node *) namedarg->arg;
! }
!
! result = coerce_type(pstate,
! node,
actual_arg_types[i],
declared_arg_types[i], -1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST,
-1);
!
! /* When original node was NamedArgExpr, pack result to named expr again. */
! if (namedarg != NULL)
! {
! NamedArgExpr *newarg = (NamedArgExpr *) makeNode(NamedArgExpr);
!
! newarg->name = namedarg->name;
! newarg->arg = (Expr *) result;
! newarg->position = namedarg->position;
! newarg->location = namedarg->location;
!
! result = (Node *) newarg;
! }
!
! lfirst(current_fargs) = result;
! }
i++;
! }
}
/*
***************
*** 1276,1282 ****
{
FuncCandidateList clist;
! clist = FuncnameGetCandidates(funcname, nargs, false, false);
while (clist)
{
--- 1452,1458 ----
{
FuncCandidateList clist;
! clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
while (clist)
{
*** ./src/backend/utils/adt/ruleutils.c.orig 2009-08-16 23:56:28.290078563 +0200
--- ./src/backend/utils/adt/ruleutils.c 2009-08-18 07:44:33.039874437 +0200
***************
*** 4323,4328 ****
--- 4323,4338 ----
case T_FuncExpr:
get_func_expr((FuncExpr *) node, context, showimplicit);
break;
+
+ case T_NamedArgExpr:
+ {
+ NamedArgExpr *expr = (NamedArgExpr *) node;
+
+ get_rule_expr((Node *) expr->arg, context, true);
+ appendStringInfo(buf, " AS ");
+ appendStringInfo(buf, expr->name);
+ }
+ break;
case T_OpExpr:
get_oper_expr((OpExpr *) node, context);
***************
*** 6374,6380 ****
* specified argtypes.
*/
p_result = func_get_detail(list_make1(makeString(proname)),
! NIL, nargs, argtypes, false, true,
&p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_true_typeids, NULL);
if ((p_result == FUNCDETAIL_NORMAL ||
--- 6384,6390 ----
* specified argtypes.
*/
p_result = func_get_detail(list_make1(makeString(proname)),
! NIL, NIL, nargs, argtypes, false, true,
&p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_true_typeids, NULL);
if ((p_result == FUNCDETAIL_NORMAL ||
*** ./src/include/catalog/namespace.h.orig 2009-08-17 21:30:23.061247506 +0200
--- ./src/include/catalog/namespace.h 2009-08-21 19:33:59.197409342 +0200
***************
*** 32,37 ****
--- 32,38 ----
int nargs; /* number of arg types returned */
int nvargs; /* number of args to become variadic array */
int ndargs; /* number of defaulted args */
+ int *proargidxs; /* array of argument's index in proargs */
Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */
} *FuncCandidateList; /* VARIABLE LENGTH STRUCT */
***************
*** 54,60 ****
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
bool expand_variadic,
bool expand_defaults);
extern bool FunctionIsVisible(Oid funcid);
--- 55,61 ----
extern Oid TypenameGetTypid(const char *typname);
extern bool TypeIsVisible(Oid typid);
! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs, List *argnames,
bool expand_variadic,
bool expand_defaults);
extern bool FunctionIsVisible(Oid funcid);
*** ./src/include/nodes/nodes.h.orig 2009-08-16 17:52:00.554081687 +0200
--- ./src/include/nodes/nodes.h 2009-08-16 23:26:23.773078179 +0200
***************
*** 123,128 ****
--- 123,129 ----
T_WindowFunc,
T_ArrayRef,
T_FuncExpr,
+ T_NamedArgExpr,
T_OpExpr,
T_DistinctExpr,
T_ScalarArrayOpExpr,
*** ./src/include/nodes/parsenodes.h.orig 2009-08-16 17:52:27.486081227 +0200
--- ./src/include/nodes/parsenodes.h 2009-08-16 23:25:15.604077084 +0200
***************
*** 273,279 ****
{
NodeTag type;
List *funcname; /* qualified name of function */
! List *args; /* the arguments (list of exprs) */
bool agg_star; /* argument was really '*' */
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
--- 273,279 ----
{
NodeTag type;
List *funcname; /* qualified name of function */
! List *args; /* the arguments (list of FuncCallArg) */
bool agg_star; /* argument was really '*' */
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
*** ./src/include/nodes/primnodes.h.orig 2009-08-16 17:57:51.019078284 +0200
--- ./src/include/nodes/primnodes.h 2009-08-22 22:22:19.096256636 +0200
***************
*** 300,305 ****
--- 300,314 ----
} CoercionForm;
/*
+ * CallNotationType - what call notation is used
+ */
+ typedef enum CallNotationType
+ {
+ CALL_NOTATION_POSITIONAL,
+ CALL_NOTATION_NAMED
+ } CallNotationType;
+
+ /*
* FuncExpr - expression node for a function call
*/
typedef struct FuncExpr
***************
*** 309,319 ****
--- 318,346 ----
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
CoercionForm funcformat; /* how to display this function call */
+ CallNotationType notation; /* call notation type */
List *args; /* arguments to the function */
int location; /* token location, or -1 if unknown */
} FuncExpr;
/*
+ * NamedArgExpr - an named argument of function
+ *
+ * It is used, when argument has name. When positional notation is used, then
+ * args list doesn't contain any NamedArgExpr. Named notation means, so all
+ * arguments in list are NamedArgExpr. Mixed notation means, so first n arguments
+ * are Exprs and last m arguments are NamedArgExpr.
+ */
+ typedef struct NamedArgExpr
+ {
+ Expr xpr;
+ Expr *arg; /* the argument */
+ char *name; /* an name of argument */
+ int position; /* position of argument in proarg list */
+ int location; /* token location, or -1 if unknown */
+ } NamedArgExpr;
+
+ /*
* OpExpr - expression node for an operator invocation
*
* Semantically, this is essentially the same as a function call.
*** ./src/include/parser/parse_func.h.orig 2009-08-18 07:37:59.809646272 +0200
--- ./src/include/parser/parse_func.h 2009-08-18 07:38:19.301876771 +0200
***************
*** 47,53 ****
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,
int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype,
--- 47,53 ----
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, List *fargnames,
int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype,
*** ./src/test/regress/expected/create_view.out.orig 2009-08-24 21:03:33.638626926 +0200
--- ./src/test/regress/expected/create_view.out 2009-08-24 21:01:55.000000000 +0200
***************
*** 282,284 ****
--- 282,319 ----
drop cascades to view mytempview
drop cascades to view pubview
SET search_path to public;
+ -- using a named parameters
+ CREATE FUNCTION nmfunc(a anyelement, b anyelement, flag bool)
+ RETURNS anyelement AS $$
+ SELECT CASE WHEN $3 THEN $1 ELSE $2 END;
+ $$ LANGUAGE sql;
+ CREATE TABLE nmtest(a int, b int, reverse bool);
+ INSERT INTO nmtest VALUES
+ (10,20, true),
+ (20,10, false);
+
+ CREATE VIEW nmview AS
+ SELECT a, b, nmfunc(a,b, reverse as flag)
+ FROM nmtest;
+
+ SELECT * FROM nmview;
+ a | b | nmfunc
+ ----+----+--------
+ 10 | 20 | 10
+ 20 | 10 | 10
+ (2 rows)
+
+ \d nmview
+ View "public.nmview"
+ Column | Type | Modifiers
+ --------+---------+-----------
+ a | integer |
+ b | integer |
+ nmfunc | integer |
+ View definition:
+ SELECT nmtest.a, nmtest.b, nmfunc(nmtest.a, nmtest.b, nmtest.reverse AS flag) AS nmfunc
+ FROM nmtest;
+
+ DROP VIEW nmview;
+ DROP TABLE nmtest;
+ DROP FUNCTION nmfunc(anyelement, anyelement, bool);
*** ./src/test/regress/expected/polymorphism.out.orig 2009-08-24 20:28:14.238626036 +0200
--- ./src/test/regress/expected/polymorphism.out 2009-08-24 20:27:03.000000000 +0200
***************
*** 837,843 ****
-- verify it lists properly
\df dfunc
! 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
--- 837,843 ----
-- verify it lists properly
\df dfunc
! 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,1011 ****
ERROR: cannot remove parameter defaults from existing function
HINT: Use DROP FUNCTION first.
\df dfunc
! List of functions
Schema | Name | Result data type | Argument data types | Type
--------+-------+------------------+-------------------------------------------------+--------
public | dfunc | integer | VARIADIC a integer[] DEFAULT ARRAY[]::integer[] | normal
--- 1005,1011 ----
ERROR: cannot remove parameter defaults from existing function
HINT: Use DROP FUNCTION first.
\df dfunc
! List of functions
Schema | Name | Result data type | Argument data types | Type
--------+-------+------------------+-------------------------------------------------+--------
public | dfunc | integer | VARIADIC a integer[] DEFAULT ARRAY[]::integer[] | normal
***************
*** 1038,1040 ****
--- 1038,1361 ----
drop function dfunc(int, int, int);
drop function dfunc(int, int);
drop function dfunc(text);
+ -- test function with named params and using named or mixed notation
+ -- fail, unnamed param behind named
+ create function dfunc(a int, b int = 1, c int) returns table (a int, b int, c int) as $$
+ select $1, $2, $3;
+ $$ language sql;
+ ERROR: input parameters after one with a default value must also have defaults
+ 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,0);
+ a | b | c | d
+ ---+---+---+---
+ 0 | 0 | 0 | 0
+ (1 row)
+
+ select * from dfunc(0,0,10 as c);
+ a | b | c | d
+ ---+---+----+---
+ 0 | 0 | 10 | 0
+ (1 row)
+
+ select * from dfunc(0,0,10 as d);
+ a | b | c | d
+ ---+---+---+----
+ 0 | 0 | 0 | 10
+ (1 row)
+
+ select * from dfunc(10 as x, 20 as b, 30 as c); --fail - unknown param
+ ERROR: function dfunc(integer, integer, integer) 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, 20 as b, 30); -- fail using positional notation begind named notation
+ ERROR: expected named argument
+ LINE 1: select * from dfunc(10, 20 as b, 30);
+ ^
+ HINT: You can't put positional arguments after named arguments.
+ select * from dfunc(10,10,20 as a); --fail - a overlaps first positional parameter
+ ERROR: function dfunc(integer, integer, integer) 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.
+ drop function dfunc(int, int, int, int);
+ -- test multitypes named params
+ create function dfunc(a varchar, b numeric, c date) returns table (a varchar, b numeric, c date) as $$
+ select $1, $2, $3;
+ $$ language sql;
+ select (dfunc('Hello World',20, to_date('2009-07-25','YYYY-MM-DD'))).*;
+ a | b | c
+ -------------+----+------------
+ Hello World | 20 | 07-25-2009
+ (1 row)
+
+ select * from dfunc('Hello World',20, to_date('2009-07-25','YYYY-MM-DD'));
+ a | b | c
+ -------------+----+------------
+ Hello World | 20 | 07-25-2009
+ (1 row)
+
+ select * from dfunc(to_date('2009-07-25','YYYY-MM-DD') 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, to_date('2009-07-25','YYYY-MM-DD') as c);
+ a | b | c
+ -------------+----+------------
+ Hello World | 20 | 07-25-2009
+ (1 row)
+
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD') as c, 20 as b);
+ a | b | c
+ -------------+----+------------
+ Hello World | 20 | 07-25-2009
+ (1 row)
+
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD') as c, 20::int as b);
+ a | b | c
+ -------------+----+------------
+ Hello World | 20 | 07-25-2009
+ (1 row)
+
+ drop function dfunc(varchar, numeric, date);
+ --test named and default params
+ create function dfunc(a varchar = 'def a', b date = 'infinity', c numeric = -1, variadic d int[] = '{}')
+ returns table (a varchar, b date, c numeric, d int[]) as $$
+ select $1, $2, $3, $4;
+ $$ language sql;
+ select (dfunc()).*;
+ a | b | c | d
+ -------+----------+----+----
+ def a | infinity | -1 | {}
+ (1 row)
+
+ select * from dfunc();
+ a | b | c | d
+ -------+----------+----+----
+ def a | infinity | -1 | {}
+ (1 row)
+
+ -- variadic parameter should be used only without using named params,
+ -- because cannot be named and have to be last => there cannot be any
+ -- named params before.
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD'), 100, 1,2,3,4,5);
+ a | b | c | d
+ -------------+------------+-----+-------------
+ Hello World | 07-25-2009 | 100 | {1,2,3,4,5}
+ (1 row)
+
+ -- defaults, variadic default and named together
+ select * from dfunc('Hello World');
+ a | b | c | d
+ -------------+----------+----+----
+ Hello World | infinity | -1 | {}
+ (1 row)
+
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD'));
+ a | b | c | d
+ -------------+------------+----+----
+ Hello World | 07-25-2009 | -1 | {}
+ (1 row)
+
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD'), 100 as c);
+ a | b | c | d
+ -------------+------------+-----+----
+ Hello World | 07-25-2009 | 100 | {}
+ (1 row)
+
+ select * from dfunc('Hello World' as a, 100 as c);
+ a | b | c | d
+ -------------+----------+-----+----
+ Hello World | infinity | 100 | {}
+ (1 row)
+
+ select * from dfunc(100 as c);
+ a | b | c | d
+ -------+----------+-----+----
+ def a | infinity | 100 | {}
+ (1 row)
+
+ select * from dfunc(to_date('2009-07-25','YYYY-MM-DD') as b);
+ a | b | c | d
+ -------+------------+----+----
+ def a | 07-25-2009 | -1 | {}
+ (1 row)
+
+ select * from dfunc(100 as c, to_date('2009-07-25','YYYY-MM-DD') as b);
+ a | b | c | d
+ -------+------------+-----+----
+ def a | 07-25-2009 | 100 | {}
+ (1 row)
+
+ select * from dfunc(to_date('2009-07-25','YYYY-MM-DD') as b, 100 as c);
+ a | b | c | d
+ -------+------------+-----+----
+ def a | 07-25-2009 | 100 | {}
+ (1 row)
+
+ select * from dfunc('Hello World', variadic array[1,2,3] as d);
+ a | b | c | d
+ -------------+----------+----+---------
+ Hello World | infinity | -1 | {1,2,3}
+ (1 row)
+
+ select * from dfunc(variadic array[1,2,3] as d);
+ a | b | c | d
+ -------+----------+----+---------
+ def a | infinity | -1 | {1,2,3}
+ (1 row)
+
+ drop function dfunc(varchar, date, numeric, int[]);
+ -- test, out parameters and 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)
+
+ drop function dfunc(varchar, numeric);
+ -- test polymorphic params
+ create function dfunc(a anyelement, b anyelement, 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, 1 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('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)
+
*** ./src/test/regress/expected/rangefuncs.out.orig 2009-08-24 19:19:22.897623869 +0200
--- ./src/test/regress/expected/rangefuncs.out 2009-08-24 20:00:14.000000000 +0200
***************
*** 515,520 ****
--- 515,526 ----
xyz | {xyz,xyz}
(1 row)
+ -- fails, an 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 existing input parameter
+ HINT: Use DROP FUNCTION first.
+ DROP FUNCTION dup(anyelement);
-- equivalent specification
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
***************
*** 830,832 ****
--- 836,872 ----
LINE 1: select * from testfoo();
^
drop function testfoo();
+ --fail, named parameter are not unique
+ create function testfoo(a int, a int) returns int as $$ select 1;$$ language sql;
+ ERROR: parameter has not unique name
+ LINE 1: create function testfoo(a int, a int) returns int as $$ sele...
+ ^
+ create function testfoo(int, out a int, out a int) returns int as $$ select 1;$$ language sql;
+ ERROR: parameter has not unique name
+ LINE 1: create function testfoo(int, out a int, out a int) returns i...
+ ^
+ create function testfoo(out a int, inout a int) returns int as $$ select 1;$$ language sql;
+ ERROR: parameter has not unique name
+ LINE 1: create function testfoo(out a int, inout a int) returns int ...
+ ^
+ create function testfoo(a int, inout a int) returns int as $$ select 1;$$ language sql;
+ ERROR: parameter has not unique name
+ LINE 1: create function testfoo(a int, inout a int) returns int as $...
+ ^
+ -- 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);
*** ./src/test/regress/sql/create_view.sql.orig 2009-08-24 21:00:45.741621458 +0200
--- ./src/test/regress/sql/create_view.sql 2009-08-24 21:01:04.774621929 +0200
***************
*** 195,197 ****
--- 195,221 ----
DROP SCHEMA testviewschm2 CASCADE;
SET search_path to public;
+
+ -- using a named parameters
+ CREATE FUNCTION nmfunc(a anyelement, b anyelement, flag bool)
+ RETURNS anyelement AS $$
+ SELECT CASE WHEN $3 THEN $1 ELSE $2 END;
+ $$ LANGUAGE sql;
+
+ CREATE TABLE nmtest(a int, b int, reverse bool);
+ INSERT INTO nmtest VALUES
+ (10,20, true),
+ (20,10, false);
+
+ CREATE VIEW nmview AS
+ SELECT a, b, nmfunc(a,b, reverse as flag)
+ FROM nmtest;
+
+ SELECT * FROM nmview;
+
+ \d nmview
+
+ DROP VIEW nmview;
+ DROP TABLE nmtest;
+ DROP FUNCTION nmfunc(anyelement, anyelement, bool);
+
*** ./src/test/regress/sql/polymorphism.sql.orig 2009-08-17 20:58:30.672247414 +0200
--- ./src/test/regress/sql/polymorphism.sql 2009-08-24 20:25:41.406621463 +0200
***************
*** 624,626 ****
--- 624,731 ----
drop function dfunc(int, int, int);
drop function dfunc(int, int);
drop function dfunc(text);
+
+ -- test function with named params and using named or mixed notation
+ -- fail, unnamed param behind named
+ create function dfunc(a int, b int = 1, c int) returns table (a int, b int, c int) as $$
+ select $1, $2, $3;
+ $$ language sql;
+
+ 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,0);
+ select * from dfunc(0,0,10 as c);
+ select * from dfunc(0,0,10 as d);
+
+ select * from dfunc(10 as x, 20 as b, 30 as c); --fail - unknown param
+ select * from dfunc(10, 20 as b, 30); -- fail using positional notation begind named notation
+ select * from dfunc(10,10,20 as a); --fail - a overlaps first positional parameter
+
+ drop function dfunc(int, int, int, int);
+
+ -- test multitypes named params
+ create function dfunc(a varchar, b numeric, c date) returns table (a varchar, b numeric, c date) as $$
+ select $1, $2, $3;
+ $$ language sql;
+
+ select (dfunc('Hello World',20, to_date('2009-07-25','YYYY-MM-DD'))).*;
+ select * from dfunc('Hello World',20, to_date('2009-07-25','YYYY-MM-DD'));
+ select * from dfunc(to_date('2009-07-25','YYYY-MM-DD') as c, 'Hello World' as a, 20 as b);
+ select * from dfunc('Hello World', 20 as b, to_date('2009-07-25','YYYY-MM-DD') as c);
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD') as c, 20 as b);
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD') as c, 20::int as b);
+
+ drop function dfunc(varchar, numeric, date);
+
+ --test named and default params
+ create function dfunc(a varchar = 'def a', b date = 'infinity', c numeric = -1, variadic d int[] = '{}')
+ returns table (a varchar, b date, c numeric, d int[]) as $$
+ select $1, $2, $3, $4;
+ $$ language sql;
+
+ select (dfunc()).*;
+ select * from dfunc();
+
+ -- variadic parameter should be used only without using named params,
+ -- because cannot be named and have to be last => there cannot be any
+ -- named params before.
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD'), 100, 1,2,3,4,5);
+
+ -- defaults, variadic default and named together
+ select * from dfunc('Hello World');
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD'));
+ select * from dfunc('Hello World', to_date('2009-07-25','YYYY-MM-DD'), 100 as c);
+
+ select * from dfunc('Hello World' as a, 100 as c);
+ select * from dfunc(100 as c);
+ select * from dfunc(to_date('2009-07-25','YYYY-MM-DD') as b);
+ select * from dfunc(100 as c, to_date('2009-07-25','YYYY-MM-DD') as b);
+ select * from dfunc(to_date('2009-07-25','YYYY-MM-DD') as b, 100 as c);
+ select * from dfunc('Hello World', variadic array[1,2,3] as d);
+ select * from dfunc(variadic array[1,2,3] as d);
+
+ drop function dfunc(varchar, date, numeric, int[]);
+
+ -- test, out parameters and 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);
+
+ drop function dfunc(varchar, numeric);
+
+ -- test polymorphic params
+ create function dfunc(a anyelement, b anyelement, 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, 1 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('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
*** ./src/test/regress/sql/rangefuncs.sql.orig 2009-08-24 19:13:53.945622879 +0200
--- ./src/test/regress/sql/rangefuncs.sql 2009-08-24 19:59:00.718622616 +0200
***************
*** 251,256 ****
--- 251,262 ----
SELECT dup('xyz'::text);
SELECT * FROM dup('xyz'::text);
+ -- fails, an 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 specification
CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
***************
*** 385,387 ****
--- 391,408 ----
select * from testfoo(); -- fail
drop function testfoo();
+
+ --fail, named parameter 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);
+