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