diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 3c8ce43..c6ece84 100644 *** a/doc/src/sgml/xfunc.sgml --- b/doc/src/sgml/xfunc.sgml *************** CREATE FUNCTION test(int, int) RETURNS i *** 1101,1106 **** --- 1101,1222 ---- + + Positional, Mixed and Named notation + + + notation + functions + + + + Functions with named parameters should by called with mixed + or named notation. Any typical use expects posiotional + notation, when an order of parameters is important. For named notation + is important name of parameters. You can mix both notation - result is mixed notation. + Some first n parameters should be entered in positional parameters + and others in named notation. You don't need pass parameters than has + default value. + + 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 obligatory parameters: a and + b and optional parameters: m and o. + This function should be called with positional parameters. See 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) + + SELECT fx(10,20,2,50); + fx + ----- + 110 + (1 row) + + + + + + Using named notation + + + function + named notation + + + + + SELECT fx(10 as a, 20 as b); + fx + ---- + 30 + (1 row) + + 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) + + + + + + Using mixed notation + + + function + mixed notation + + + + + 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..714d337 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) *** 603,610 **** * The caller might end up discarding such an entry anyway, but if it selects * such an entry it should react as though the call were ambiguous. */ FuncCandidateList ! FuncnameGetCandidates(List *names, int nargs, bool expand_variadic, bool expand_defaults) { FuncCandidateList resultList = NULL; --- 604,723 ---- * The caller might end up discarding such an entry anyway, but if it selects * such an entry it should react as though the call were ambiguous. */ + 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; + FuncCallNotation used_notation = POSITIONAL_NOTATION; /* used only for asertion */ + + #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); + + /* + * An number less or equal nargs means some arg, + * an number greather than nargs means some default. + */ + *param_map = palloc(pronargs * sizeof(short int)); + for (i = 0; i < pronargs; i++) + (*param_map)[i] = UNDEFINED_PARAMETER; + + 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)) + continue; + if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0) + { + /* protect under twice entry same param via 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, fast leaving */ + if (!found) + return false; + + used_notation = NAMED_NOTATION; + } + else + { + Assert(used_notation == POSITIONAL_NOTATION); + + (*param_map)[ap] = ap; /* positional parameter */ + } + ap++; + } + + Assert(used_notation == NAMED_NOTATION); + + /* Have we check defaults? */ + 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; + } + + 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)) { --- 758,811 ---- 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 **** --- 845,851 ---- 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? --- 859,872 ---- } 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) { --- 1063,1069 ---- visible = false; clist = FuncnameGetCandidates(list_make1(makeString(proname)), ! nargs, NIL, false, false); for (; clist; clist = clist->next) { *************** FunctionIsVisible(Oid funcid) *** 953,959 **** /* ! * OpernameGetOprid * Given a possibly-qualified operator name and exact input datatypes, * look up the operator. Returns InvalidOid if not found. * --- 1084,1090 ---- /* ! * OpernameGetOpri * Given a possibly-qualified operator name and exact input datatypes, * look up the operator. Returns InvalidOid if not found. * 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 1976648..89e506b 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 8b466f4..ee7f7f8 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..9f178b7 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 be75590..5dcabf4 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(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(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 *** 2271,2277 **** */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, ! &args, false, context); if (simple) /* successfully simplified it */ { --- 2274,2280 ---- */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, ! &args, false, false, context); if (simple) /* successfully simplified it */ { *************** eval_const_expressions_mutator(Node *nod *** 2463,2469 **** simple = simplify_function(outfunc, CSTRINGOID, -1, ! &args, true, context); if (simple) /* successfully simplified output fn */ { --- 2466,2472 ---- simple = simplify_function(outfunc, CSTRINGOID, -1, ! &args, false, true, context); if (simple) /* successfully simplified output fn */ { *************** eval_const_expressions_mutator(Node *nod *** 2481,2487 **** simple = simplify_function(infunc, expr->resulttype, -1, ! &args, true, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; --- 2484,2490 ---- simple = simplify_function(infunc, expr->resulttype, -1, ! &args, false, true, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; *************** simplify_boolean_equality(List *args) *** 3226,3231 **** --- 3229,3235 ---- 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 *** 3247,3254 **** 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); --- 3251,3258 ---- 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 *** 3271,3277 **** * 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); --- 3275,3281 ---- * 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 *** 3297,3310 **** 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); /* --- 3301,3341 ---- 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 *** 3443,3448 **** --- 3474,3480 ---- 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 858e16c..ddfe95d 100644 *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** static TypeName *TableFuncTypeName(List *** 419,424 **** --- 419,427 ---- %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 *** 8794,8800 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8797,8803 ---- 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 *** 8818,8824 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8821,8827 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' arg_expr_list ',' VARIADIC a_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** func_expr: func_name '(' ')' over_clause *** 8830,8836 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' ALL expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8833,8839 ---- 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 *** 8846,8852 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' DISTINCT expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8849,8855 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' DISTINCT arg_expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** expr_list: a_expr *** 9568,9573 **** --- 9571,9601 ---- $$ = lappend($1, $3); } ; + + /* 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 *** 9968,9974 **** ; ! name: ColId { $$ = $1; }; database_name: ColId { $$ = $1; }; --- 9996,10002 ---- ; ! name: ColId { $$ = $1; }; database_name: ColId { $$ = $1; }; *************** func_name: type_function_name *** 10001,10007 **** /* ! * Constants */ AexprConst: Iconst { --- 10029,10035 ---- /* ! * Constants ToDo - PStehule */ AexprConst: Iconst { *************** AexprConst: Iconst *** 10035,10044 **** 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); --- 10063,10080 ---- 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..8ad5a2c 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 then once", name), ! errhint("Check used names of next parameters."), ! parser_errposition(pstate, exprLocation(targ)))); ! } ! } ! } ! else ! { ! targ = transformExpr(pstate, arg); ! if (notation != POSITIONAL_NOTATION) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("expected named expression"), ! errhint("You can't put positionals arguments behind the 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..556d4fe 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_select_candidate(int nargs, *** 809,814 **** --- 846,852 ---- 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; --- 855,864 ---- 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); /* --- 873,879 ---- *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 **** --- 1027,1040 ---- 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 **** --- 1108,1115 ---- /* 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) { --- 1326,1332 ---- { 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 333cb25..d5877a5 100644 *** a/src/backend/utils/adt/ruleutils.c --- b/src/backend/utils/adt/ruleutils.c *************** generate_function_name(Oid funcid, int n *** 6372,6380 **** * specified argtypes. */ p_result = func_get_detail(list_make1(makeString(proname)), ! NIL, nargs, argtypes, false, true, &p_funcid, &p_rettype, ! &p_retset, &p_nvargs, &p_true_typeids, NULL); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE || p_result == FUNCDETAIL_WINDOWFUNC) && --- 6372,6380 ---- * 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 9d53ab9..84e91e0 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..9c60aaa 100644 *** a/src/test/regress/expected/polymorphism.out --- b/src/test/regress/expected/polymorphism.out *************** select dfunc('Hi'); *** 1038,1040 **** --- 1038,1104 ---- 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 expression + LINE 1: select * from dfunc(10, 20 as b, 30); + ^ + HINT: You can't put positionals arguments behind the 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); diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql index c01871d..ff83723 100644 *** a/src/test/regress/sql/polymorphism.sql --- b/src/test/regress/sql/polymorphism.sql *************** select dfunc('Hi'); *** 624,626 **** --- 624,650 ---- 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);