*** ./doc/src/sgml/syntax.sgml.orig 2009-10-02 15:45:01.696472450 +0200 --- ./doc/src/sgml/syntax.sgml 2009-10-02 15:45:51.603472385 +0200 *************** *** 1505,1510 **** --- 1505,1514 ---- The list of built-in functions is in . Other functions can be added by the user. + + + See for more details. + *** ./doc/src/sgml/xfunc.sgml.orig 2009-10-02 11:09:52.888415595 +0200 --- ./doc/src/sgml/xfunc.sgml 2009-10-02 15:06:35.066231252 +0200 *************** *** 1101,1106 **** --- 1101,1275 ---- + + Calling Functions + + + notation + functions + + + + PostgreSQL supports calling functions with named + parameters using 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. The + positional notation calls the function with its + argument values in the same order as they are defined in the function + declaration. Using named notation allows passing + arguments to a function by name, in any order. These + named and positional + notations can be mixed (mixed notation). However, + positional notation is only allowed from left to the + right in the argument list. As soon as a named + argument appears in the list, named notation must be + used for all remaining arguments. Arguments with default values can be + omitted. The default values of such arguments will then be used instead. + The following examples 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 + how to call a function with default values. + + + + Using positional notation + + + function + positional notation + + + + Positional notation is the traditional mechanism for + passing arguments 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 have its default value + false assigned implicitly. 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 + true will be assigned to the argument + uppercase explicitly, and the function wil concatenate + 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. The advantage of using + named notation is that the arguments may be specified in + any order, 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) + + + + + Variadic functions cannot be called using named + or mixed notation. + + + + + + Using mixed notation + + + function + mixed notation + + + + The mixed notation combines + positional and named + notation. However, as already mentioned, named arguments cannot precede + positional arguments. + + SELECT concat_lower_or_upper('Hello', 'World' AS b, true AS uppercase); + concat_lower_or_upper + ----------------------- + HELLO WORLD + (1 row) + + The above query is legal, since the argument a is + specified using positional notation, before any + named arguments. However, the following example will fail, since + 'Hello' is specified using positional notation after 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-10-02 11:09:52.901413816 +0200 --- ./src/backend/catalog/namespace.c 2009-10-02 15:35:03.111310678 +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,722 ---- int effective_nargs; int pathpos = 0; bool variadic; ! bool use_defaults = false; /* make 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 ! * ! * Variadic functions can't be called using named or mixed ! * notation. ! */ ! if (OidIsValid(procform->provariadic)) ! continue; ! ! 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 **** --- 756,771 ---- newResult->pathpos = pathpos; newResult->oid = HeapTupleGetOid(proctup); newResult->nargs = effective_nargs; + + /* + * Save proargidxs in newResult. It's needed later to adjust + * the argument types to be the types corresponding to the + * named arguments (if any), and also to assign positions to + * any NamedArgExpr arguments after the best candidate is + * determined. The former could be done here, but we leave + * both for the caller to do. + */ + newResult->proargidxs = proargidxs; memcpy(newResult->args, procform->proargtypes.values, pronargs * sizeof(Oid)); if (variadic) *************** *** 886,891 **** --- 930,1065 ---- } /* + * 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); + + /* + * pronargs equal to nargs means explicit arguments (no defaults) + */ + *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++; + } + + /* + * This function is only called for named and mixed notation, and + * the last argument must be named in either case. + */ + 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) { --- 1106,1112 ---- 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-10-02 11:09:52.908413835 +0200 --- ./src/backend/catalog/pg_aggregate.c 2009-10-02 11:10:41.002289003 +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-10-02 11:09:52.918291271 +0200 --- ./src/backend/catalog/pg_proc.c 2009-10-02 15:36:10.194311900 +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,525 ---- newlc = lnext(newlc); } } + + /* + * If there are named parameters, check to make sure the names + * have not been changed. 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-09-23 01:43:37.000000000 +0200 --- ./src/backend/nodes/copyfuncs.c 2009-10-02 11:10:41.005288929 +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 * *************** *** 3575,3580 **** --- 3592,3600 ---- 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-09-23 01:43:38.000000000 +0200 --- ./src/backend/nodes/equalfuncs.c 2009-10-02 11:10:41.006289067 +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); *************** *** 2365,2370 **** --- 2377,2385 ---- 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-10-02 11:09:52.947291561 +0200 --- ./src/backend/nodes/makefuncs.c 2009-10-02 11:10:41.007290532 +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-10-02 11:09:52.949290938 +0200 --- ./src/backend/nodes/nodeFuncs.c 2009-10-02 11:10:41.019290724 +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-09-17 22:49:28.000000000 +0200 --- ./src/backend/nodes/outfuncs.c 2009-10-02 11:10:41.021288487 +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"); *************** *** 2514,2519 **** --- 2526,2534 ---- 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-10-02 11:09:52.978412980 +0200 --- ./src/backend/nodes/readfuncs.c 2009-10-02 11:10:41.022289952 +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-10-02 11:09:52.987288528 +0200 --- ./src/backend/optimizer/util/clauses.c 2009-10-02 15:37:57.858436383 +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 changes the order of any arg in arglist. When some ! * arguments are missing, then it uses the 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-09-23 01:43:38.000000000 +0200 --- ./src/backend/parser/gram.y 2009-10-02 15:59:40.739471599 +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 *************** *** 4748,4754 **** func_args_list: func_arg { $$ = list_make1($1); } ! | func_args_list ',' func_arg { $$ = lappend($1, $3); } ; /* --- 4753,4759 ---- func_args_list: func_arg { $$ = list_make1($1); } ! | func_args_list ',' func_arg { $$ = AppendFuncParameter($1, $3, @3, yyscanner); } ; /* *************** *** 4762,4769 **** func_args_with_defaults_list: func_arg_with_default { $$ = list_make1($1); } ! | func_args_with_defaults_list ',' func_arg_with_default ! { $$ = lappend($1, $3); } ; /* --- 4767,4773 ---- 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); } ; /* *************** *** 8944,8950 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8948,8954 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' func_arg_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 8956,8962 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' VARIADIC a_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8960,8966 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' VARIADIC func_arg_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 8968,8974 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8972,8978 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 8980,8986 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' ALL expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 8984,8990 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' ALL func_arg_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 8996,9002 **** n->location = @1; $$ = (Node *)n; } ! | func_name '(' DISTINCT expr_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 9000,9006 ---- n->location = @1; $$ = (Node *)n; } ! | func_name '(' DISTINCT func_arg_list ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 9719,9724 **** --- 9723,9755 ---- } ; + /* 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); } ; *************** *** 10185,10194 **** 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); --- 10216,10237 ---- 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); *************** *** 11288,11293 **** --- 11331,11376 ---- } /* + * Append function parameter to function's parameter list. Reject + * parameters with non-unique 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 non-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-10-02 11:09:53.006291862 +0200 --- ./src/backend/parser/parse_expr.c 2009-10-02 11:10:41.031289311 +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-10-02 11:09:53.020292459 +0200 --- ./src/backend/parser/parse_func.c 2009-10-02 15:41:52.163310884 +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 non-unique name"), + errhint("Check 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); + } + } + + /* forget 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,1168 ---- 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); ! } ! ! /* ! * Drop unused defaults (overwritten by positional or named parameters). ! * In this moment we don't need to respect real pgproc order, because ! * these values (argdefaults and true_typeids) are used only for type coercion ! * They are appended to real parameter list. ! */ ! 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++; ! } } /* --- 1208,1253 ---- /* 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) { --- 1454,1460 ---- { FuncCandidateList clist; ! clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false); while (clist) { *** ./src/backend/utils/adt/regproc.c.orig 2009-10-02 11:09:53.032288094 +0200 --- ./src/backend/utils/adt/regproc.c 2009-10-02 11:10:41.034289027 +0200 *************** *** 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-10-02 11:09:53.046402067 +0200 --- ./src/backend/utils/adt/ruleutils.c 2009-10-02 11:10:41.036288885 +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-10-02 11:09:53.047289182 +0200 --- ./src/include/catalog/namespace.h 2009-10-02 11:10:41.038290557 +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-10-02 11:09:53.049291212 +0200 --- ./src/include/nodes/nodes.h 2009-10-02 11:10:41.039288671 +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-10-02 11:09:53.050289190 +0200 --- ./src/include/nodes/parsenodes.h 2009-10-02 11:10:41.040288879 +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-10-02 11:09:53.051290939 +0200 --- ./src/include/nodes/primnodes.h 2009-10-02 15:42:29.213472437 +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,347 ---- 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 + * + * Used when argument has name. When positional notation is used, then + * args list doesn't contain any NamedArgExpr. When named notation is + * used, all arguments in list are NamedArgExpr. When mixed notation + * is used, the 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-10-02 11:09:53.053289129 +0200 --- ./src/include/parser/parse_func.h 2009-10-02 11:10:41.042289085 +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-10-02 11:09:53.054290947 +0200 --- ./src/test/regress/expected/create_view.out 2009-10-02 11:10:41.043288805 +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-10-02 11:09:53.056289067 +0200 --- ./src/test/regress/expected/polymorphism.out 2009-10-02 11:10:41.045289151 +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,1273 ---- 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, 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-10-02 11:09:53.057291374 +0200 --- ./src/test/regress/expected/rangefuncs.out 2009-10-02 15:43:34.972596742 +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 non-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 non-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 non-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 non-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-10-02 11:09:53.058289003 +0200 --- ./src/test/regress/sql/create_view.sql 2009-10-02 11:10:41.046288940 +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-10-02 11:09:53.060291382 +0200 --- ./src/test/regress/sql/polymorphism.sql 2009-10-02 11:10:41.047289148 +0200 *************** *** 624,626 **** --- 624,702 ---- 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, 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-10-02 11:09:53.061289011 +0200 --- ./src/test/regress/sql/rangefuncs.sql 2009-10-02 11:10:41.048291101 +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); +