diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 3c8ce43..fe03247 100644
*** a/doc/src/sgml/xfunc.sgml
--- b/doc/src/sgml/xfunc.sgml
*************** CREATE FUNCTION test(int, int) RETURNS i
*** 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
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 2b0cb35..a180b42 100644
*** a/src/backend/catalog/namespace.c
--- b/src/backend/catalog/namespace.c
***************
*** 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"
*************** TypeIsVisible(Oid typid)
*** 557,564 ****
/*
* FuncnameGetCandidates
! * Given a possibly-qualified function name and argument count,
* retrieve a list of the possible matches.
*
* If nargs is -1, we return all functions matching the given name,
--- 558,696 ----
/*
+ * 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.
+ *
+ * The param_map array is initialized with the number of parameters
+ * by the given function and contains the position of a positional
+ * or named argument. A positional argument is initialized to
+ * the same array index of its position within the list, a named
+ * argument entry stores its relative position within the argument list.
+ * An argument which was not defined in the given calling argument list
+ * is set to -1.
+ */
+ static bool
+ VerifyCandidateNamedNotation(HeapTuple proctup, int pronargs, int nargs, List *argnames, short int pronargdefaults,
+ bool *use_defaults, short int **param_map)
+ {
+ 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;
+
+ /* used for assertion only */
+ FuncCallNotation used_notation = POSITIONAL_NOTATION;
+
+ #define UNDEFINED_PARAMETER -1
+
+ Assert(argnames != NIL);
+
+ /* Ignore if not enough default expressions */
+ if (nargs + pronargdefaults < pronargs)
+ 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,
+ * a number greater than nargs means there are defaults, too.
+ */
+ *param_map = palloc(pronargs * sizeof(short int));
+ MemSet(*param_map, UNDEFINED_PARAMETER, pronargs * sizeof(short int));
+
+ 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 against dublicated entries by positional and named notation */
+ if ((*param_map)[pp] != UNDEFINED_PARAMETER)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("named parameter \"%s\" overlaps %d. positional parameter", argname, i + 1)));
+
+ found = true;
+ (*param_map)[pp] = ap; /* named parameter */
+ break;
+ }
+ /* increase only for IN and INOUT args */
+ pp++;
+ }
+ /* any name isn't in proargnames, abort */
+ if (!found)
+ return false;
+
+ used_notation = NAMED_NOTATION;
+ }
+ else
+ {
+ Assert(used_notation == POSITIONAL_NOTATION);
+
+ /* positional parameter */
+ (*param_map)[ap] = ap;
+ }
+ ap++;
+ }
+
+ Assert(used_notation == NAMED_NOTATION);
+
+ /* 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 ((*param_map)[i] == UNDEFINED_PARAMETER)
+ {
+ if (i < first_arg_with_default)
+ return false;
+ /* offset to defaults + nargs */
+ (*param_map)[i] = i - first_arg_with_default + nargs;
+ }
+ }
+ *use_defaults = true;
+ }
+
+ return true;
+ }
+
+ /*
* FuncnameGetCandidates
! * Given a possibly-qualified function name and argument count,
* retrieve a list of the possible matches.
*
* If nargs is -1, we return all functions matching the given name,
*************** TypeIsVisible(Oid typid)
*** 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;
--- 736,742 ----
* 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;
*************** FuncnameGetCandidates(List *names, int n
*** 645,688 ****
int pronargs = procform->pronargs;
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))
{
--- 777,830 ----
int pronargs = procform->pronargs;
int effective_nargs;
int pathpos = 0;
! bool variadic = false;
! bool use_defaults = false;
! Oid va_elem_type = InvalidOid;
FuncCandidateList newResult;
+ short int *param_map = NULL;
! if (argnames != NIL)
{
! if (!VerifyCandidateNamedNotation(proctup, pronargs, nargs, argnames,
! procform->pronargdefaults, &use_defaults, ¶m_map))
! continue;
}
else
{
! /*
! * Check if function is variadic, if true, get variadic element type.
! * If expand_variadic is false, we 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))
{
*************** FuncnameGetCandidates(List *names, int n
*** 722,727 ****
--- 864,870 ----
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = effective_nargs;
+ newResult->param_map = param_map;
memcpy(newResult->args, procform->proargtypes.values,
pronargs * sizeof(Oid));
if (variadic)
*************** FuncnameGetCandidates(List *names, int n
*** 735,741 ****
}
else
newResult->nvargs = 0;
! newResult->ndargs = use_defaults ? pronargs - nargs : 0;
/*
* Does it have the same arguments as something we already accepted?
--- 878,892 ----
}
else
newResult->nvargs = 0;
!
! /* When named notation is used, then complete set of defaults is returned */
! if (argnames != NIL)
! {
! Assert(param_map != NULL);
! newResult->ndargs = procform->pronargdefaults;
! }
! else
! newResult->ndargs = use_defaults ? pronargs - nargs : 0;
/*
* Does it have the same arguments as something we already accepted?
*************** FunctionIsVisible(Oid funcid)
*** 932,938 ****
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! nargs, false, false);
for (; clist; clist = clist->next)
{
--- 1083,1089 ----
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! nargs, NIL, false, false);
for (; clist; clist = clist->next)
{
*************** OpernameGetCandidates(List *names, char
*** 1204,1209 ****
--- 1355,1361 ----
newResult->ndargs = 0;
newResult->args[0] = operform->oprleft;
newResult->args[1] = operform->oprright;
+ newResult->param_map = NULL;
newResult->next = resultList;
resultList = newResult;
}
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 845322e..26651d5 100644
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
*************** lookup_agg_function(List *fnName,
*** 321,329 ****
* 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);
/* only valid case is a normal function not returning a set */
if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
--- 321,329 ----
* 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, NULL, NULL);
/* only valid case is a normal function not returning a set */
if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7b7383b..b7cb98e 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyFuncExpr(FuncExpr *from)
*** 1013,1018 ****
--- 1013,1019 ----
COPY_SCALAR_FIELD(funcresulttype);
COPY_SCALAR_FIELD(funcretset);
COPY_SCALAR_FIELD(funcformat);
+ COPY_SCALAR_FIELD(argformat);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
*************** _copyFuncCall(FuncCall *from)
*** 1913,1918 ****
--- 1914,1930 ----
return newnode;
}
+ static ArgExpr *
+ _copyArgExpr(ArgExpr *from)
+ {
+ ArgExpr *newnode = makeNode(ArgExpr);
+
+ COPY_STRING_FIELD(name);
+ COPY_NODE_FIELD(expr);
+
+ return newnode;
+ }
+
static A_Star *
_copyAStar(A_Star *from)
{
*************** copyObject(void *from)
*** 4012,4017 ****
--- 4024,4032 ----
case T_FuncCall:
retval = _copyFuncCall(from);
break;
+ case T_ArgExpr:
+ retval = _copyArgExpr(from);
+ break;
case T_A_Star:
retval = _copyAStar(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 33d5db0..42f4f4e 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalFuncExpr(FuncExpr *a, FuncExpr *b)
*** 235,240 ****
--- 235,241 ----
b->funcformat != COERCE_DONTCARE)
return false;
+ COMPARE_SCALAR_FIELD(argformat);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 9ed9018..de816b6 100644
*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
*************** makeFuncExpr(Oid funcid, Oid rettype, Li
*** 342,347 ****
--- 342,348 ----
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = false; /* only allowed case here */
funcexpr->funcformat = fformat;
+ funcexpr->argformat = CONTINUOUS_LIST;
funcexpr->args = args;
funcexpr->location = -1;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6030292..6749a8a 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outFuncExpr(StringInfo str, FuncExpr *n
*** 871,876 ****
--- 871,877 ----
WRITE_OID_FIELD(funcresulttype);
WRITE_BOOL_FIELD(funcretset);
WRITE_ENUM_FIELD(funcformat, CoercionForm);
+ WRITE_ENUM_FIELD(argformat, ArgumentForm);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
}
*************** _outFuncCall(StringInfo str, FuncCall *n
*** 1795,1800 ****
--- 1796,1810 ----
}
static void
+ _outArgExpr(StringInfo str, ArgExpr *node)
+ {
+ WRITE_NODE_TYPE("ARGEXPR");
+
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(expr);
+ }
+
+ static void
_outDefElem(StringInfo str, DefElem *node)
{
WRITE_NODE_TYPE("DEFELEM");
*************** _outNode(StringInfo str, void *obj)
*** 2765,2770 ****
--- 2775,2783 ----
case T_FuncCall:
_outFuncCall(str, obj);
break;
+ case T_ArgExpr:
+ _outArgExpr(str, obj);
+ break;
case T_DefElem:
_outDefElem(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 4f7b740..71cca3e 100644
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
*************** _readFuncExpr(void)
*** 519,524 ****
--- 519,525 ----
READ_OID_FIELD(funcresulttype);
READ_BOOL_FIELD(funcretset);
READ_ENUM_FIELD(funcformat, CoercionForm);
+ READ_ENUM_FIELD(argformat, ArgumentForm);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index e288877..0b73c59 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** static List *simplify_and_arguments(List
*** 95,105 ****
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,
--- 95,107 ----
static Expr *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod, List **args,
! bool leaky_list, bool allow_inline,
eval_const_expressions_context *context);
! static List *add_function_defaults(List *args,
! bool leaky_list,
! 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,
*************** eval_const_expressions_mutator(Node *nod
*** 2133,2139 ****
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
! &args,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
--- 2135,2141 ----
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
! &args, expr->argformat == LEAKY_LIST,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
*************** eval_const_expressions_mutator(Node *nod
*** 2148,2153 ****
--- 2150,2156 ----
newexpr->funcresulttype = expr->funcresulttype;
newexpr->funcretset = expr->funcretset;
newexpr->funcformat = expr->funcformat;
+ newexpr->argformat = expr->argformat;
newexpr->args = args;
newexpr->location = expr->location;
return (Node *) newexpr;
*************** eval_const_expressions_mutator(Node *nod
*** 2180,2186 ****
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
! &args,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
--- 2183,2189 ----
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
! &args, false,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
*************** eval_const_expressions_mutator(Node *nod
*** 2273,2279 ****
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
! &args,
false, context);
if (simple) /* successfully simplified it */
{
--- 2276,2282 ----
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
! &args, false,
false, context);
if (simple) /* successfully simplified it */
{
*************** eval_const_expressions_mutator(Node *nod
*** 2465,2471 ****
simple = simplify_function(outfunc,
CSTRINGOID, -1,
! &args,
true, context);
if (simple) /* successfully simplified output fn */
{
--- 2468,2474 ----
simple = simplify_function(outfunc,
CSTRINGOID, -1,
! &args, false,
true, context);
if (simple) /* successfully simplified output fn */
{
*************** eval_const_expressions_mutator(Node *nod
*** 2483,2489 ****
simple = simplify_function(infunc,
expr->resulttype, -1,
! &args,
true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
--- 2486,2492 ----
simple = simplify_function(infunc,
expr->resulttype, -1,
! &args, false,
true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
*************** simplify_boolean_equality(Oid opno, List
*** 3250,3255 ****
--- 3253,3259 ----
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
List **args,
+ bool leaky_list,
bool allow_inline,
eval_const_expressions_context *context)
{
*************** simplify_function(Oid funcid, Oid result
*** 3271,3278 ****
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,
func_tuple, context);
--- 3275,3282 ----
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) || leaky_list)
! *args = add_function_defaults(*args, leaky_list, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
func_tuple, context);
*************** simplify_function(Oid funcid, Oid result
*** 3295,3301 ****
* just like the parser did.
*/
static List *
! add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
--- 3299,3305 ----
* just like the parser did.
*/
static List *
! add_function_defaults(List *args, bool leaky_list, Oid result_type, HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
*************** add_function_defaults(List *args, Oid re
*** 3321,3334 ****
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
! /* Delete any unused defaults from the list */
! ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
! if (ndelete < 0)
! elog(ERROR, "not enough default arguments");
! while (ndelete-- > 0)
! defaults = list_delete_first(defaults);
! /* And form the combined argument list */
! args = list_concat(args, defaults);
Assert(list_length(args) == funcform->pronargs);
/*
--- 3325,3365 ----
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
!
! if (leaky_list)
! {
! List *cargs = NIL; /* continuous argument list */
! ListCell *lc;
! int i = 0;
! bool first_default = funcform->pronargs - funcform->pronargdefaults;
!
! /* Replace gaps with elements from defaults */
! foreach(lc, args)
! {
! Node *arg = (Node *) lfirst(lc);
!
! if (arg == NULL)
! {
! Assert(i >= first_default);
! cargs = lappend(cargs, list_nth(defaults, i - first_default));
! }
! else
! cargs = lappend(cargs, arg);
! i++;
! }
! args = cargs;
! }
! else
! {
! /* 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);
/*
*************** evaluate_function(Oid funcid, Oid result
*** 3467,3472 ****
--- 3498,3504 ----
newexpr->funcresulttype = result_type;
newexpr->funcretset = false;
newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
+ newexpr->funcformat = CONTINUOUS_LIST;
newexpr->args = args;
newexpr->location = -1;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 58bb8d1..d5fdcf3 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static TypeName *TableFuncTypeName(List
*** 423,428 ****
--- 423,431 ----
%type opt_existing_window_name
%type opt_frame_clause frame_extent frame_bound
+ %type arg_expr_list
+ %type arg_expr
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
*************** func_expr: func_name '(' ')' over_clause
*** 8875,8881 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8878,8884 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' arg_expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** func_expr: func_name '(' ')' over_clause
*** 8887,8893 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' VARIADIC a_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8890,8896 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' VARIADIC arg_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** func_expr: func_name '(' ')' over_clause
*** 8899,8905 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8902,8908 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' arg_expr_list ',' VARIADIC arg_expr ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** func_expr: func_name '(' ')' over_clause
*** 8911,8917 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' ALL expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8914,8920 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' ALL arg_expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** func_expr: func_name '(' ')' over_clause
*** 8927,8933 ****
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' DISTINCT expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
--- 8930,8936 ----
n->location = @1;
$$ = (Node *)n;
}
! | func_name '(' DISTINCT arg_expr_list ')' over_clause
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
*************** expr_list: a_expr
*** 9650,9655 ****
--- 9653,9683 ----
}
;
+ /* Used for support of named notation.
+ */
+ arg_expr_list: arg_expr
+ {
+ $$ = list_make1($1);
+ }
+ | arg_expr_list ',' arg_expr
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
+ arg_expr: a_expr
+ {
+ $$ = $1;
+ }
+ | a_expr AS param_name
+ {
+ ArgExpr *ae = makeNode(ArgExpr);
+ ae->expr = $1;
+ ae->name = $3;
+ $$ = (Node *) ae;
+ }
+ ;
+
type_list: Typename { $$ = list_make1($1); }
| type_list ',' Typename { $$ = lappend($1, $3); }
;
*************** name_list: name
*** 10049,10055 ****
;
! name: ColId { $$ = $1; };
database_name:
ColId { $$ = $1; };
--- 10077,10083 ----
;
! name: ColId { $$ = $1; };
database_name:
ColId { $$ = $1; };
*************** func_name: type_function_name
*** 10080,10089 ****
}
;
-
- /*
- * Constants
- */
AexprConst: Iconst
{
$$ = makeIntConst($1, @1);
--- 10108,10113 ----
*************** AexprConst: Iconst
*** 10116,10125 ****
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);
--- 10140,10157 ----
t->location = @1;
$$ = makeStringConstCast($2, @2, t);
}
! | func_name '(' arg_expr_list ')' Sconst
{
/* generic syntax with a type modifier */
TypeName *t = makeTypeNameFromNameList($1);
+ ListCell *lc;
+
+ /* Don't allow ArgExpr in this context */
+ foreach(lc, $3)
+ {
+ if (IsA((Node *) lfirst(lc),ArgExpr))
+ elog(ERROR, "don't use named parameters in this context");
+ }
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 428adbd..1469f39 100644
*** a/src/backend/parser/parse_expr.c
--- b/src/backend/parser/parse_expr.c
*************** transformIndirection(ParseState *pstate,
*** 362,368 ****
list_make1(n),
list_make1(result),
false, false, false,
! NULL, true, -1);
}
}
/* process trailing subscripts, if any */
--- 362,368 ----
list_make1(n),
list_make1(result),
false, false, false,
! NULL, true, NIL, -1);
}
}
/* process trailing subscripts, if any */
*************** transformColumnRef(ParseState *pstate, C
*** 506,512 ****
list_make1(makeString(name2)),
list_make1(node),
false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 506,512 ----
list_make1(makeString(name2)),
list_make1(node),
false, false, false,
! NULL, true, NIL, cref->location);
}
break;
}
*************** transformColumnRef(ParseState *pstate, C
*** 547,553 ****
list_make1(makeString(name3)),
list_make1(node),
false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 547,553 ----
list_make1(makeString(name3)),
list_make1(node),
false, false, false,
! NULL, true, NIL, cref->location);
}
break;
}
*************** transformColumnRef(ParseState *pstate, C
*** 602,608 ****
list_make1(makeString(name4)),
list_make1(node),
false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 602,608 ----
list_make1(makeString(name4)),
list_make1(node),
false, false, false,
! NULL, true, NIL, cref->location);
}
break;
}
*************** transformAExprIn(ParseState *pstate, A_E
*** 1091,1107 ****
static Node *
transformFuncCall(ParseState *pstate, FuncCall *fn)
{
! List *targs;
ListCell *args;
/* Transform the list of arguments ... */
- targs = NIL;
foreach(args, fn->args)
{
! targs = lappend(targs, transformExpr(pstate,
! (Node *) lfirst(args)));
}
/* ... and hand off to ParseFuncOrColumn */
return ParseFuncOrColumn(pstate,
fn->funcname,
--- 1091,1158 ----
static Node *
transformFuncCall(ParseState *pstate, FuncCall *fn)
{
! List *targs = NIL;
ListCell *args;
+ List *argnames = NIL;
+ bool argnames_used = false;
+ FuncCallNotation notation = POSITIONAL_NOTATION;
/* Transform the list of arguments ... */
foreach(args, fn->args)
{
! char *name = NULL;
! Node *targ = NULL;
! Node *arg = lfirst(args);
!
! if (IsA(arg, ArgExpr))
! {
! ArgExpr *argexpr = (ArgExpr *) arg;
! ListCell *lc;
!
! Assert(argexpr->name != NULL);
!
! argnames_used = true;
! notation = NAMED_NOTATION;
!
! name = argexpr->name;
! targ = transformExpr(pstate, argexpr->expr);
!
! /* Check duplicates */
! for_each_cell(lc, lnext(args))
! {
! if (IsA(lfirst(lc), ArgExpr))
! {
! char *next_name = ((ArgExpr *) lfirst(lc))->name;
!
! Assert(next_name != NULL);
! if (strcmp(name, next_name) == 0)
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("function parameter \"%s\" is used more than once", name),
! errhint("Check your named parameters for ambiguous argument names"),
! parser_errposition(pstate, exprLocation(targ))));
! }
! }
! }
! else
! {
! targ = transformExpr(pstate, arg);
! if (notation != POSITIONAL_NOTATION)
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("expected named argument"),
! errhint("You can't put positional arguments after named arguments."),
! parser_errposition(pstate, exprLocation(targ))));
! }
!
! targs = lappend(targs, targ);
! argnames = lappend(argnames, name);
}
+ /* forgot list of NULLs */
+ if (!argnames_used)
+ argnames = NIL;
+
/* ... and hand off to ParseFuncOrColumn */
return ParseFuncOrColumn(pstate,
fn->funcname,
*************** transformFuncCall(ParseState *pstate, Fu
*** 1111,1116 ****
--- 1162,1168 ----
fn->func_variadic,
fn->over,
false,
+ argnames,
fn->location);
}
*************** transformCaseExpr(ParseState *pstate, Ca
*** 1167,1173 ****
Node *warg;
Assert(IsA(w, CaseWhen));
-
warg = (Node *) w->expr;
if (placeholder)
{
--- 1219,1224 ----
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 8ea76ab..0cec812 100644
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
*************** static void unknown_attribute(ParseState
*** 59,65 ****
Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, int location)
{
Oid rettype;
Oid funcid;
--- 59,65 ----
Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, List *argnames, int location)
{
Oid rettype;
Oid funcid;
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 75,80 ****
--- 75,83 ----
bool retset;
int nvargs;
FuncDetailCode fdresult;
+ short int *param_map;
+ int pronargs;
+ ArgumentForm argformat = CONTINUOUS_LIST;
/*
* Most of the rest of the parser just assumes that functions do not have
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 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];
--- 133,139 ----
* wasn't any aggregate or variadic decoration.
*/
if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
! !func_variadic && list_length(funcname) == 1 && argnames == NIL)
{
Oid argtype = actual_arg_types[0];
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 161,170 ****
* 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);
if (fdresult == FUNCDETAIL_COERCION)
{
/*
--- 164,173 ----
* 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, argnames, nargs, actual_arg_types,
!func_variadic, true,
&funcid, &rettype, &retset, &nvargs,
! &declared_arg_types, &argdefaults, ¶m_map, &pronargs);
if (fdresult == FUNCDETAIL_COERCION)
{
/*
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 241,269 ****
parser_errposition(pstate, location)));
}
! /*
! * If there are default arguments, we have to include their types in
! * actual_arg_types for the purpose of checking generic type consistency.
! * However, we do NOT put them into the generated parse node, because
! * their actual values might change before the query gets run. The
! * planner has to insert the up-to-date values at plan time.
! */
! nargsplusdefs = nargs;
! foreach(l, argdefaults)
{
! Node *expr = (Node *) lfirst(l);
! /* probably shouldn't happen ... */
! if (nargsplusdefs >= FUNC_MAX_ARGS)
! ereport(ERROR,
! (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
! errmsg_plural("cannot pass more than %d argument to a function",
! "cannot pass more than %d arguments to a function",
! FUNC_MAX_ARGS,
! FUNC_MAX_ARGS),
! parser_errposition(pstate, location)));
! actual_arg_types[nargsplusdefs++] = exprType(expr);
}
/*
--- 244,305 ----
parser_errposition(pstate, location)));
}
!
! if (param_map != NULL)
{
! List *rfargs = NIL; /* reordered list of function arguments */
! int i;
! for (i = 0; i < pronargs; i++)
! {
! Node *expr = NULL;
! if (param_map[i] < nargs)
! {
! expr = (Node *) list_nth(fargs, param_map[i]);
! rfargs = lappend(rfargs, expr);
! /* when any arg goes out of narg */
! if (i >= nargs)
! argformat = LEAKY_LIST;
! }
! else
! {
! expr = (Node *) list_nth(argdefaults, param_map[i] - nargs);
! rfargs = lappend(rfargs, NULL);
! }
! actual_arg_types[i] = exprType(expr);
! }
!
! fargs = (argformat == LEAKY_LIST) ? rfargs : list_truncate(rfargs, nargs);
! nargsplusdefs = pronargs;
! }
! else
! {
! /*
! * If there are default arguments, we have to include their types in
! * actual_arg_types for the purpose of checking generic type consistency.
! * However, we do NOT put them into the generated parse node, because
! * their actual values might change before the query gets run. The
! * planner has to insert the up-to-date values at plan time.
! */
!
! nargsplusdefs = nargs;
! foreach(l, argdefaults)
! {
! Node *expr = (Node *) lfirst(l);
!
! /* probably shouldn't happen ... */
! if (nargsplusdefs >= FUNC_MAX_ARGS)
! ereport(ERROR,
! (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
! errmsg_plural("cannot pass more than %d argument to a function",
! "cannot pass more than %d arguments to a function",
! FUNC_MAX_ARGS,
! FUNC_MAX_ARGS),
! parser_errposition(pstate, location)));
!
! actual_arg_types[nargsplusdefs++] = exprType(expr);
! }
}
/*
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 319,324 ****
--- 355,361 ----
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = retset;
funcexpr->funcformat = COERCE_EXPLICIT_CALL;
+ funcexpr->argformat = argformat;
funcexpr->args = fargs;
funcexpr->location = location;
*************** func_match_argtypes(int nargs,
*** 446,457 ****
current_candidate = next_candidate)
{
next_candidate = current_candidate->next;
! if (can_coerce_type(nargs, input_typeids, current_candidate->args,
! COERCION_IMPLICIT))
{
! current_candidate->next = *candidates;
! *candidates = current_candidate;
! ncandidates++;
}
}
--- 483,528 ----
current_candidate = next_candidate)
{
next_candidate = current_candidate->next;
!
! /*
! * When named params are used, we have to remap argtypes too.
! */
! if (current_candidate->param_map == NULL)
{
! if (can_coerce_type(nargs, input_typeids, current_candidate->args,
! COERCION_IMPLICIT))
! {
! current_candidate->next = *candidates;
! *candidates = current_candidate;
! ncandidates++;
! }
! }
! else
! {
! int i;
! Oid args[FUNC_MAX_ARGS];
! short int *param_map = current_candidate->param_map;
!
! /*
! * When any named parameter is used, then candidate param_map and args
! * has funcdef items always. Using an default parameter doesn't change
! * this fact. When dafaults are used, then input_typeids is truncated
! * to number of calling parameters.
! */
! Assert(nargs < FUNC_MAX_ARGS);
! Assert(nargs <= current_candidate->nargs);
!
! /* adjust position for all defined params */
! for (i = 0; i < current_candidate->nargs; i++)
! args[param_map[i]] = current_candidate->args[i];
!
! /* but check ony calling params */
! if (can_coerce_type(nargs, input_typeids, args, COERCION_IMPLICIT))
! {
! current_candidate->next = *candidates;
! *candidates = current_candidate;
! ncandidates++;
! }
}
}
*************** func_select_candidate(int nargs,
*** 809,814 ****
--- 880,886 ----
FuncDetailCode
func_get_detail(List *funcname,
List *fargs,
+ List *argnames,
int nargs,
Oid *argtypes,
bool expand_variadic,
*************** func_get_detail(List *funcname,
*** 817,824 ****
Oid *rettype, /* return value */
bool *retset, /* return value */
int *nvargs, /* return value */
! Oid **true_typeids, /* return value */
! List **argdefaults) /* optional return value */
{
FuncCandidateList raw_candidates;
FuncCandidateList best_candidate;
--- 889,898 ----
Oid *rettype, /* return value */
bool *retset, /* return value */
int *nvargs, /* return value */
! Oid **true_typeids, /* return value */
! List **argdefaults, /* optional return value */
! short int **param_map, /* optional return value */
! int *pronargs) /* optional return value */
{
FuncCandidateList raw_candidates;
FuncCandidateList best_candidate;
*************** func_get_detail(List *funcname,
*** 833,839 ****
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
! raw_candidates = FuncnameGetCandidates(funcname, nargs,
expand_variadic, expand_defaults);
/*
--- 907,913 ----
*argdefaults = NIL;
/* Get list of possible candidates from namespace search */
! raw_candidates = FuncnameGetCandidates(funcname, nargs, argnames,
expand_variadic, expand_defaults);
/*
*************** func_get_detail(List *funcname,
*** 987,992 ****
--- 1061,1074 ----
pform = (Form_pg_proc) GETSTRUCT(ftup);
*rettype = pform->prorettype;
*retset = pform->proretset;
+
+ if (param_map)
+ {
+ Assert(pronargs != NULL);
+ *pronargs = best_candidate->nargs;
+ *param_map = best_candidate->param_map;
+ }
+
/* fetch default args if caller wants 'em */
if (argdefaults)
{
*************** make_fn_arguments(ParseState *pstate,
*** 1060,1065 ****
--- 1142,1149 ----
/* types don't match? then force coercion using a function call... */
if (actual_arg_types[i] != declared_arg_types[i])
{
+ Assert(lfirst(current_fargs) != NULL);
+
lfirst(current_fargs) = coerce_type(pstate,
lfirst(current_fargs),
actual_arg_types[i],
*************** LookupFuncName(List *funcname, int nargs
*** 1276,1282 ****
{
FuncCandidateList clist;
! clist = FuncnameGetCandidates(funcname, nargs, false, false);
while (clist)
{
--- 1360,1366 ----
{
FuncCandidateList clist;
! clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
while (clist)
{
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 0817e6a..dc58062 100644
*** a/src/backend/utils/adt/regproc.c
--- b/src/backend/utils/adt/regproc.c
*************** regprocin(PG_FUNCTION_ARGS)
*** 131,137 ****
* pg_proc entries in the current search path.
*/
names = stringToQualifiedNameList(pro_name_or_oid);
! clist = FuncnameGetCandidates(names, -1, false, false);
if (clist == NULL)
ereport(ERROR,
--- 131,137 ----
* pg_proc entries in the current search path.
*/
names = stringToQualifiedNameList(pro_name_or_oid);
! clist = FuncnameGetCandidates(names, -1, NIL, false, false);
if (clist == NULL)
ereport(ERROR,
*************** regprocout(PG_FUNCTION_ARGS)
*** 190,196 ****
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! -1, false, false);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
--- 190,196 ----
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
! -1, NIL, false, false);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
*************** regprocedurein(PG_FUNCTION_ARGS)
*** 277,283 ****
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
! clist = FuncnameGetCandidates(names, nargs, false, false);
for (; clist; clist = clist->next)
{
--- 277,283 ----
*/
parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
! 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 9005209..4402163 100644
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** generate_function_name(Oid funcid, int n
*** 6361,6369 ****
* 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 ||
p_result == FUNCDETAIL_AGGREGATE ||
p_result == FUNCDETAIL_WINDOWFUNC) &&
--- 6361,6369 ----
* 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, NULL, NULL);
if ((p_result == FUNCDETAIL_NORMAL ||
p_result == FUNCDETAIL_AGGREGATE ||
p_result == FUNCDETAIL_WINDOWFUNC) &&
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 956069d..a1cb70d 100644
*** a/src/include/catalog/namespace.h
--- b/src/include/catalog/namespace.h
*************** typedef struct _FuncCandidateList
*** 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 */
+ short int *param_map; /* maps external arguments to function arguments */
Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */
} *FuncCandidateList; /* VARIABLE LENGTH STRUCT */
*************** extern Oid TypenameGetTypid(const char *
*** 55,62 ****
extern bool TypeIsVisible(Oid typid);
extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
! bool expand_variadic,
! bool expand_defaults);
extern bool FunctionIsVisible(Oid funcid);
extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
--- 56,64 ----
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);
extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 925375b..ff7f799 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 376,381 ****
--- 376,382 ----
T_XmlSerialize,
T_WithClause,
T_CommonTableExpr,
+ T_ArgExpr,
/*
* TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f7e6939..b602404 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct FuncCall
*** 282,287 ****
--- 282,305 ----
} FuncCall;
/*
+ * ArgExpr - an argument of function
+ */
+ typedef struct ArgExpr
+ {
+ NodeTag type;
+ char *name; /* an name of argument (when is specified) */
+ Node *expr; /* the argument */
+ } ArgExpr;
+
+
+ /* notation used for Func call params */
+ typedef enum FuncCallNotation
+ {
+ POSITIONAL_NOTATION,
+ NAMED_NOTATION
+ } FuncCallNotation;
+
+ /*
* A_Star - '*' representing all columns of a table or compound field
*
* This can appear within ColumnRef.fields, A_Indirection.indirection, and
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 6e7f52b..a1bdf0d 100644
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
*************** typedef enum CoercionForm
*** 299,304 ****
--- 299,310 ----
COERCE_DONTCARE /* special case for planner */
} CoercionForm;
+ typedef enum ArgumentForm
+ {
+ CONTINUOUS_LIST, /* used for positional notation */
+ LEAKY_LIST /* used for named and mixed notation */
+ } ArgumentForm;
+
/*
* FuncExpr - expression node for a function call
*/
*************** typedef struct FuncExpr
*** 309,314 ****
--- 315,321 ----
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
CoercionForm funcformat; /* how to display this function call */
+ ArgumentForm argformat; /* what is format of argument list */
List *args; /* arguments to the function */
int location; /* token location, or -1 if unknown */
} FuncExpr;
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 2a49f00..940ed28 100644
*** a/src/include/parser/parse_func.h
--- b/src/include/parser/parse_func.h
*************** typedef enum
*** 45,58 ****
extern Node *ParseFuncOrColumn(ParseState *pstate,
List *funcname, List *fargs,
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,
bool *retset, int *nvargs, Oid **true_typeids,
! List **argdefaults);
extern int func_match_argtypes(int nargs,
Oid *input_typeids,
--- 45,58 ----
extern Node *ParseFuncOrColumn(ParseState *pstate,
List *funcname, List *fargs,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, List *argnames, int location);
! extern FuncDetailCode func_get_detail(List *funcname, List *fargs, List *argnames,
! int nargs, Oid *argtypes,
bool expand_variadic, bool expand_defaults,
Oid *funcid, Oid *rettype,
bool *retset, int *nvargs, Oid **true_typeids,
! List **argdefaults, short int **param_map, int *pronargs);
extern int func_match_argtypes(int nargs,
Oid *input_typeids,
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 77f693c..49f79f3 100644
*** a/src/test/regress/expected/polymorphism.out
--- b/src/test/regress/expected/polymorphism.out
*************** select dfunc();
*** 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
*************** $$ select array_upper($1, 1) $$ language
*** 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
*************** select dfunc('Hi');
*** 1038,1040 ****
--- 1038,1287 ----
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: named parameter "a" overlaps 1. 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'))).*;
+ 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);
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index c01871d..5bf2f4f 100644
*** a/src/test/regress/sql/polymorphism.sql
--- b/src/test/regress/sql/polymorphism.sql
*************** select dfunc('Hi');
*** 624,626 ****
--- 624,712 ----
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);
+
+