diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 3c8ce43..be0ae19 100644 *** a/doc/src/sgml/xfunc.sgml --- b/doc/src/sgml/xfunc.sgml *************** CREATE FUNCTION test(int, int) RETURNS i *** 1101,1106 **** --- 1101,1251 ---- + + 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 + sections will illustrate the usage of all three kinds of notation. + + + + CREATE FUNCTION fx(a int, b int, m int = 1, o int = 0) RETURNS int AS $$ + SELECT ($1 + $2) * $3 + $4 + $$ LANGUAGE SQL; + + Function fx has two mandatory parameters: a and + b. Additionally there are two optional parameters: + m and o, with default values 1 for the argument + m and 0 for the last argument o. + 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 + + + + + SELECT fx(10,20); + fx + ---- + 30 + (1 row) + + The example above calls the function fx with both mandatory arguments + a and b in positional notation + and omits all default arguments. Both arguments m and o will + get their default values 1 and 0 assigned. + + SELECT fx(10,20,2,50); + fx + ----- + 110 + (1 row) + + Now the function fx is called with all arguments in positional notation. + In this case the argument m and o will have the values 2 and + 50 assigned. + + + + + Using named notation + + + function + named notation + + + + + SELECT fx(10 as a, 20 as b); + fx + ---- + 30 + (1 row) + + Using named notation this time, the mandatory arguments a and b + are specified with the ASkeyword. + + SELECT fx(20 as b, 10 as a); + fx + ---- + 30 + (1 row) + + SELECT fx(20 as b, 10 as a, 50 as o); + fx + ---- + 80 + (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. + + + + + 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 fx(10,20, 50 as o); + fx + ---- + 80 + (1 row) + + SELECT fx(10,20, 10 as m); + fx + ----- + 300 + (1 row) + + SELECT fx(10,20, 50 as o, 2 as m); + fx + ----- + 110 + (1 row) + + + + + Function Volatility Categories diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 2b0cb35..4d6bded 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 we still missing param 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, 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)) { *************** 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,891 ---- } 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) { --- 1082,1088 ---- visible = false; clist = FuncnameGetCandidates(list_make1(makeString(proname)), ! nargs, NIL, false, false); for (; clist; clist = clist->next) { *************** OpernameGetCandidates(List *names, char *** 1204,1209 **** --- 1354,1360 ---- 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); + +