*** ./doc/src/sgml/xfunc.sgml.orig 2009-03-05 07:37:10.000000000 +0100 --- ./doc/src/sgml/xfunc.sgml 2009-03-05 08:30:48.000000000 +0100 *************** *** 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 *** ./src/backend/catalog/namespace.c.orig 2009-03-03 14:48:25.000000000 +0100 --- ./src/backend/catalog/namespace.c 2009-03-04 18:31:20.000000000 +0100 *************** *** 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" *************** *** 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; *************** *** 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)) { *************** *** 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) *************** *** 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? *************** *** 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) { *************** *** 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. * *** ./src/backend/catalog/pg_aggregate.c.orig 2009-03-03 16:44:18.000000000 +0100 --- ./src/backend/catalog/pg_aggregate.c 2009-03-04 14:15:08.000000000 +0100 *************** *** 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)) *** ./src/backend/nodes/copyfuncs.c.orig 2009-03-03 13:11:22.000000000 +0100 --- ./src/backend/nodes/copyfuncs.c 2009-03-04 14:25:54.000000000 +0100 *************** *** 1009,1014 **** --- 1009,1015 ---- COPY_SCALAR_FIELD(funcresulttype); COPY_SCALAR_FIELD(funcretset); COPY_SCALAR_FIELD(funcformat); + COPY_SCALAR_FIELD(argformat); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); *************** *** 1906,1911 **** --- 1907,1923 ---- 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) { *************** *** 4026,4031 **** --- 4038,4046 ---- case T_FuncCall: retval = _copyFuncCall(from); break; + case T_ArgExpr: + retval = _copyArgExpr(from); + break; case T_A_Star: retval = _copyAStar(from); break; *** ./src/backend/nodes/equalfuncs.c.orig 2009-03-04 14:28:58.000000000 +0100 --- ./src/backend/nodes/equalfuncs.c 2009-03-04 14:31:26.000000000 +0100 *************** *** 235,240 **** --- 235,241 ---- b->funcformat != COERCE_DONTCARE) return false; + COMPARE_SCALAR_FIELD(argformat); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); *** ./src/backend/nodes/makefuncs.c.orig 2009-03-04 14:26:40.000000000 +0100 --- ./src/backend/nodes/makefuncs.c 2009-03-04 14:27:54.000000000 +0100 *************** *** 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; *** ./src/backend/nodes/outfuncs.c.orig 2009-03-03 13:11:23.000000000 +0100 --- ./src/backend/nodes/outfuncs.c 2009-03-04 14:31:06.000000000 +0100 *************** *** 866,871 **** --- 866,872 ---- 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); } *************** *** 1786,1791 **** --- 1787,1801 ---- } 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"); *************** *** 2764,2769 **** --- 2774,2782 ---- case T_FuncCall: _outFuncCall(str, obj); break; + case T_ArgExpr: + _outArgExpr(str, obj); + break; case T_DefElem: _outDefElem(str, obj); break; *** ./src/backend/nodes/readfuncs.c.orig 2009-03-04 14:29:23.000000000 +0100 --- ./src/backend/nodes/readfuncs.c 2009-03-04 14:32:00.000000000 +0100 *************** *** 518,523 **** --- 518,524 ---- 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); *** ./src/backend/optimizer/util/clauses.c.orig 2009-03-04 14:21:48.000000000 +0100 --- ./src/backend/optimizer/util/clauses.c 2009-03-04 17:58:19.000000000 +0100 *************** *** 94,103 **** bool *haveNull, bool *forceFalse); 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, --- 94,104 ---- bool *haveNull, bool *forceFalse); 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, *************** *** 2132,2138 **** */ simple = simplify_function(expr->funcid, expr->funcresulttype, exprTypmod(node), ! &args, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; --- 2133,2139 ---- */ simple = simplify_function(expr->funcid, expr->funcresulttype, exprTypmod(node), ! &args, expr->argformat == LEAKY_LIST, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; *************** *** 2147,2152 **** --- 2148,2154 ---- 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; *************** *** 2179,2185 **** */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, ! &args, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; --- 2181,2187 ---- */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, ! &args, false, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; *************** *** 2270,2276 **** */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, ! &args, false, context); if (simple) /* successfully simplified it */ { --- 2272,2278 ---- */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, ! &args, false, false, context); if (simple) /* successfully simplified it */ { *************** *** 2462,2468 **** simple = simplify_function(outfunc, CSTRINGOID, -1, ! &args, true, context); if (simple) /* successfully simplified output fn */ { --- 2464,2470 ---- simple = simplify_function(outfunc, CSTRINGOID, -1, ! &args, false, true, context); if (simple) /* successfully simplified output fn */ { *************** *** 2480,2486 **** simple = simplify_function(infunc, expr->resulttype, -1, ! &args, true, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; --- 2482,2488 ---- simple = simplify_function(infunc, expr->resulttype, -1, ! &args, false, true, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; *************** *** 3225,3230 **** --- 3227,3233 ---- 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) { *************** *** 3246,3253 **** 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); --- 3249,3256 ---- 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); *************** *** 3270,3276 **** * 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); --- 3273,3279 ---- * 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); *************** *** 3296,3309 **** 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); /* --- 3299,3339 ---- 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); /* *************** *** 3442,3447 **** --- 3472,3478 ---- newexpr->funcresulttype = result_type; newexpr->funcretset = false; newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */ + newexpr->funcformat = CONTINUOUS_LIST; newexpr->args = args; newexpr->location = -1; *** ./src/backend/parser/gram.y.orig 2009-02-24 11:06:33.000000000 +0100 --- ./src/backend/parser/gram.y 2009-03-03 13:08:21.000000000 +0100 *************** *** 420,425 **** --- 420,428 ---- %type opt_existing_window_name %type opt_frame_clause frame_extent frame_bound + %type arg_expr_list + %type arg_expr + /* * If you make any token changes, update the keyword table in *************** *** 8776,8782 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8779,8785 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' arg_expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 8800,8806 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8803,8809 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' arg_expr_list ',' VARIADIC a_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 8812,8818 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' ALL expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8815,8821 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' ALL arg_expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 8828,8834 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' DISTINCT expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8831,8837 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' DISTINCT arg_expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 9550,9555 **** --- 9553,9583 ---- $$ = 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); } *************** *** 9950,9956 **** ; ! name: ColId { $$ = $1; }; database_name: ColId { $$ = $1; }; --- 9978,9984 ---- ; ! name: ColId { $$ = $1; }; database_name: ColId { $$ = $1; }; *************** *** 9980,9986 **** /* ! * Constants */ AexprConst: Iconst { --- 10008,10014 ---- /* ! * Constants ToDo - PStehule */ AexprConst: Iconst { *************** *** 10014,10023 **** 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); --- 10042,10059 ---- 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); *** ./src/backend/parser/parse_expr.c.orig 2009-03-03 12:56:38.000000000 +0100 --- ./src/backend/parser/parse_expr.c 2009-03-03 14:18:12.000000000 +0100 *************** *** 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 */ *************** *** 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; } *************** *** 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; } *************** *** 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; } *************** *** 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, *************** *** 1111,1116 **** --- 1162,1168 ---- fn->func_variadic, fn->over, false, + argnames, fn->location); } *************** *** 1167,1173 **** Node *warg; Assert(IsA(w, CaseWhen)); - warg = (Node *) w->expr; if (placeholder) { --- 1219,1224 ---- *** ./src/backend/parser/parse_func.c.orig 2009-03-03 12:56:45.000000000 +0100 --- ./src/backend/parser/parse_func.c 2009-03-04 15:46:52.000000000 +0100 *************** *** 63,69 **** 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; --- 63,69 ---- 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; *************** *** 79,84 **** --- 79,87 ---- 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 *************** *** 132,138 **** * 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]; --- 135,141 ---- * 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]; *************** *** 163,172 **** * 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) { /* --- 166,175 ---- * 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) { /* *************** *** 243,248 **** --- 246,281 ---- 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. *************** *** 250,269 **** * 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("cannot pass more than %d arguments to a function", ! FUNC_MAX_ARGS), ! parser_errposition(pstate, location))); ! actual_arg_types[nargsplusdefs++] = exprType(expr); } /* --- 283,304 ---- * 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("cannot pass more than %d arguments to a function", ! FUNC_MAX_ARGS), ! parser_errposition(pstate, location))); ! actual_arg_types[nargsplusdefs++] = exprType(expr); ! } } /* *************** *** 319,324 **** --- 354,360 ---- funcexpr->funcresulttype = rettype; funcexpr->funcretset = retset; funcexpr->funcformat = COERCE_EXPLICIT_CALL; + funcexpr->argformat = argformat; funcexpr->args = fargs; funcexpr->location = location; *************** *** 807,812 **** --- 843,849 ---- FuncDetailCode func_get_detail(List *funcname, List *fargs, + List *argnames, int nargs, Oid *argtypes, bool expand_variadic, *************** *** 816,828 **** bool *retset, /* return value */ int *nvargs, /* return value */ Oid **true_typeids, /* return value */ ! List **argdefaults) /* optional return value */ { FuncCandidateList raw_candidates; FuncCandidateList best_candidate; /* Get list of possible candidates from namespace search */ ! raw_candidates = FuncnameGetCandidates(funcname, nargs, expand_variadic, expand_defaults); /* --- 853,867 ---- 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; /* Get list of possible candidates from namespace search */ ! raw_candidates = FuncnameGetCandidates(funcname, nargs, argnames, expand_variadic, expand_defaults); /* *************** *** 976,981 **** --- 1015,1028 ---- 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) { *************** *** 1141,1146 **** --- 1188,1195 ---- /* 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], *************** *** 1357,1363 **** { FuncCandidateList clist; ! clist = FuncnameGetCandidates(funcname, nargs, false, false); while (clist) { --- 1406,1412 ---- { FuncCandidateList clist; ! clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false); while (clist) { *** ./src/backend/utils/adt/regproc.c.orig 2009-03-03 14:56:09.000000000 +0100 --- ./src/backend/utils/adt/regproc.c 2009-03-03 14:57:02.000000000 +0100 *************** *** 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, *************** *** 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; *************** *** 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) { *** ./src/backend/utils/adt/ruleutils.c.orig 2009-03-03 14:56:02.000000000 +0100 --- ./src/backend/utils/adt/ruleutils.c 2009-03-04 14:17:33.000000000 +0100 *************** *** 6322,6330 **** * 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) && --- 6322,6330 ---- * 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) && *** ./src/include/catalog/namespace.h.orig 2009-03-03 14:49:23.000000000 +0100 --- ./src/include/catalog/namespace.h 2009-03-04 09:14:42.000000000 +0100 *************** *** 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 */ *************** *** 54,60 **** extern Oid TypenameGetTypid(const char *typname); extern bool TypeIsVisible(Oid typid); ! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs, bool expand_variadic, bool expand_defaults); extern bool FunctionIsVisible(Oid funcid); --- 55,61 ---- extern Oid TypenameGetTypid(const char *typname); extern bool TypeIsVisible(Oid typid); ! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs, List *argnames, bool expand_variadic, bool expand_defaults); extern bool FunctionIsVisible(Oid funcid); *** ./src/include/nodes/nodes.h.orig 2009-02-25 04:30:37.000000000 +0100 --- ./src/include/nodes/nodes.h 2009-03-03 13:13:42.000000000 +0100 *************** *** 378,383 **** --- 378,384 ---- T_XmlSerialize, T_WithClause, T_CommonTableExpr, + T_ArgExpr, /* * TAGS FOR RANDOM OTHER STUFF *** ./src/include/nodes/parsenodes.h.orig 2009-03-03 13:13:22.000000000 +0100 --- ./src/include/nodes/parsenodes.h 2009-03-03 14:15:03.000000000 +0100 *************** *** 289,294 **** --- 289,312 ---- } 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 *** ./src/include/nodes/primnodes.h.orig 2009-03-04 14:12:16.000000000 +0100 --- ./src/include/nodes/primnodes.h 2009-03-04 14:13:43.000000000 +0100 *************** *** 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 */ *************** *** 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; *** ./src/include/parser/parse_func.h.orig 2009-03-03 14:16:15.000000000 +0100 --- ./src/include/parser/parse_func.h 2009-03-04 11:52:54.000000000 +0100 *************** *** 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, *** ./src/test/regress/expected/polymorphism.out.orig 2009-03-04 18:57:59.000000000 +0100 --- ./src/test/regress/expected/polymorphism.out 2009-03-04 18:57:14.000000000 +0100 *************** *** 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); *** ./src/test/regress/sql/polymorphism.sql.orig 2009-03-04 18:48:30.000000000 +0100 --- ./src/test/regress/sql/polymorphism.sql 2009-03-04 18:56:35.000000000 +0100 *************** *** 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);