Hello this version implements syntax based on argmodes.
CREATE FUNCTION mleast(variadic numeric[]) RETURNS numeric AS $$ SELECT min($1[i]) FROM generate_subscripts($1,1) g(i); $$ LANGUAGE SQL; Regards Pavel Stehule 2008/6/24 Tom Lane <[EMAIL PROTECTED]>: > Andrew Dunstan <[EMAIL PROTECTED]> writes: >> But if I have >> foo( a text, b int[]) >> it looks odd if both these calls are legal: >> foo('a',1,2,3,) >> foo('a',ARRAY[1,2,3]) >> which I understand would be the case with the current patch. > > Maybe I misunderstand what is supposed to happen, but I believe that > if the function is marked VARIADIC then the second case would in fact > be rejected: the signature of the function for parameter-matching > purposes is text followed by one or more ints, never text and int[]. > >> I'm also still curious to know how the following would be handled: >> foo(a text[], b text[]) > > I think a is just text[], full stop. Only the last parameter is > interpreted differently for variadic. > > Your point about the syntax is good though. It would be better if > the syntax were like > > create function foo (a text, variadic b int[]) > > or maybe even better > > create function foo (a text, variadic b int) > > since (a) this makes it much more obvious to the reader what the > function might match, and (b) it leaves the door open for marking > multiple parameters as variadic, if we can figure out what that means. > > regards, tom lane >
*** ./doc/src/sgml/ref/create_function.sgml.orig 2008-06-24 16:46:47.000000000 +0200 --- ./doc/src/sgml/ref/create_function.sgml 2008-06-24 16:47:46.000000000 +0200 *************** *** 102,108 **** <listitem> <para> The mode of an argument: either <literal>IN</>, <literal>OUT</>, ! or <literal>INOUT</>. If omitted, the default is <literal>IN</>. </para> </listitem> </varlistentry> --- 102,109 ---- <listitem> <para> The mode of an argument: either <literal>IN</>, <literal>OUT</>, ! <literal>INOUT</> or <literal>VARIADIC</literal>. If omitted, ! the default is <literal>IN</>. </para> </listitem> </varlistentry> *** ./doc/src/sgml/xfunc.sgml.orig 2008-06-24 16:53:58.000000000 +0200 --- ./doc/src/sgml/xfunc.sgml 2008-06-24 16:59:42.000000000 +0200 *************** *** 578,584 **** <para> Parameters can be marked as <literal>IN</> (the default), ! <literal>OUT</>, or <literal>INOUT</>. An <literal>INOUT</> parameter serves as both an input parameter (part of the calling argument list) and an output parameter (part of the result record type). </para> --- 578,585 ---- <para> Parameters can be marked as <literal>IN</> (the default), ! <literal>OUT</>, <literal>INOUT</>, or <literal>VARIADIC</literal>. ! An <literal>INOUT</> parameter serves as both an input parameter (part of the calling argument list) and an output parameter (part of the result record type). </para> *************** *** 805,810 **** --- 806,833 ---- </screen> </para> </sect2> + + <sect2> + <title>Variadic <acronym>SQL</acronym> Functions</title> + + <para> + <acronym>SQL</acronym> functions can be declared to accept + variable number of arguments. + <screen> + CREATE FUNCTION mleast(variadic numeric[]) RETURNS numeric AS $$ + SELECT min($1[i]) + FROM generate_subscripts($1,1) g(i); + $$ LANGUAGE SQL; + + SELECT mleast(10, -1, 5, 4); + mleast + -------- + -1 + (1 row) + </screen> + </para> + </sect2> + </sect1> <sect1 id="xfunc-overload"> *** ./src/backend/catalog/namespace.c.orig 2008-06-24 11:24:34.000000000 +0200 --- ./src/backend/catalog/namespace.c 2008-06-24 13:58:31.000000000 +0200 *************** *** 570,576 **** * identical entries in later namespaces. */ FuncCandidateList ! FuncnameGetCandidates(List *names, int nargs) { FuncCandidateList resultList = NULL; char *schemaname; --- 570,576 ---- * identical entries in later namespaces. */ FuncCandidateList ! FuncnameGetCandidates(List *names, int nargs, bool transform_variadic) { FuncCandidateList resultList = NULL; char *schemaname; *************** *** 606,614 **** int pronargs = procform->pronargs; int pathpos = 0; FuncCandidateList newResult; /* Ignore if it doesn't match requested argument count */ ! if (nargs >= 0 && pronargs != nargs) continue; if (OidIsValid(namespaceId)) --- 606,655 ---- int pronargs = procform->pronargs; int pathpos = 0; FuncCandidateList newResult; + Oid va_oid = InvalidOid; + bool variadic = false; + bool isnull; + Datum proargmodes; + + /* + * Search type of variadic argument, + */ + proargmodes = SysCacheGetAttr(PROCOID, proctup, + Anum_pg_proc_proargmodes, &isnull); + if (!isnull) + { + ArrayType *ar = DatumGetArrayTypeP(proargmodes); + char *argmodes; + int j; + + argmodes = ARR_DATA_PTR(ar); + for (j = 0; j < ARR_DIMS(ar)[0]; j++) + if (argmodes[j] == PROARGMODE_VARIADIC) + { + variadic = true; + switch (procform->proargtypes.values[j]) + { + case ANYOID: + va_oid = ANYOID; + break; + case ANYARRAYOID: + va_oid = ANYELEMENTOID; + break; + default: + va_oid = get_element_type(procform->proargtypes.values[j]); + Assert(OidIsValid(va_oid)); + } + + break; + } + } /* Ignore if it doesn't match requested argument count */ ! if (nargs >= 0 && pronargs != nargs && !variadic) ! continue; ! ! /* Ignore variadic function with less arguments */ ! if (nargs >= 0 && pronargs > nargs && variadic) continue; if (OidIsValid(namespaceId)) *************** *** 691,706 **** /* * Okay to add it to result list */ ! newResult = (FuncCandidateList) ! palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid) ! + pronargs * sizeof(Oid)); newResult->pathpos = pathpos; newResult->oid = HeapTupleGetOid(proctup); - newResult->nargs = pronargs; - memcpy(newResult->args, procform->proargtypes.values, - pronargs * sizeof(Oid)); - newResult->next = resultList; resultList = newResult; } --- 732,772 ---- /* * Okay to add it to result list */ ! if (variadic && transform_variadic) ! { ! int i; ! ! Assert(nargs >= pronargs); ! ! newResult = (FuncCandidateList) ! palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid) ! + nargs * sizeof(Oid)); ! newResult->nargs = nargs; ! newResult->nvargs = nargs - pronargs + 1; ! newResult->variadic_oid = va_oid; ! memcpy(newResult->args, procform->proargtypes.values, ! (pronargs - 1) * sizeof(Oid)); ! ! /* Multiply variadic argument */ ! for (i = pronargs - 1; i < nargs; i++) ! newResult->args[i] = va_oid; ! } ! else ! { ! newResult = (FuncCandidateList) ! palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid) ! + pronargs * sizeof(Oid)); ! newResult->nargs = pronargs; ! newResult->nvargs = 0; ! newResult->variadic_oid = 0; ! memcpy(newResult->args, procform->proargtypes.values, ! pronargs * sizeof(Oid)); ! } ! newResult->pathpos = pathpos; newResult->oid = HeapTupleGetOid(proctup); newResult->next = resultList; + resultList = newResult; } *************** *** 755,761 **** visible = false; ! clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs); for (; clist; clist = clist->next) { --- 821,827 ---- visible = false; ! clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs, false); for (; clist; clist = clist->next) { *** ./src/backend/catalog/pg_aggregate.c.orig 2008-06-24 14:17:42.000000000 +0200 --- ./src/backend/catalog/pg_aggregate.c 2008-06-24 15:58:27.000000000 +0200 *************** *** 297,302 **** --- 297,304 ---- FuncDetailCode fdresult; AclResult aclresult; int i; + int nvargs; + Oid va_oid; /* * func_get_detail looks up the function in the catalogs, does *************** *** 307,313 **** */ fdresult = func_get_detail(fnName, NIL, nargs, input_types, &fnOid, rettype, &retset, ! &true_oid_array); /* only valid case is a normal function not returning a set */ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) --- 309,316 ---- */ fdresult = func_get_detail(fnName, NIL, nargs, input_types, &fnOid, rettype, &retset, ! &true_oid_array, ! &nvargs, &va_oid); /* only valid case is a normal function not returning a set */ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) *************** *** 321,326 **** --- 324,334 ---- errmsg("function %s returns a set", func_signature_string(fnName, nargs, input_types)))); + if (nvargs > 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("aggregate function has variadic argument %d %d", nvargs, va_oid))); + /* * If there are any polymorphic types involved, enforce consistency, and * possibly refine the result type. It's OK if the result is still *** ./src/backend/commands/functioncmds.c.orig 2008-06-24 10:36:44.000000000 +0200 --- ./src/backend/commands/functioncmds.c 2008-06-24 12:37:26.000000000 +0200 *************** *** 173,178 **** --- 173,179 ---- Datum *paramNames; int outCount = 0; bool have_names = false; + int varCount = 0; ListCell *x; int i; *************** *** 227,241 **** errmsg("functions cannot accept set arguments"))); if (fp->mode != FUNC_PARAM_OUT) inTypes[inCount++] = toid; ! if (fp->mode != FUNC_PARAM_IN) { if (outCount == 0) /* save first OUT param's type */ *requiredResultType = toid; outCount++; } allTypes[i] = ObjectIdGetDatum(toid); paramModes[i] = CharGetDatum(fp->mode); --- 228,270 ---- errmsg("functions cannot accept set arguments"))); if (fp->mode != FUNC_PARAM_OUT) + { inTypes[inCount++] = toid; + /* check if variadic argument is last IN argument */ + if (varCount > 0 && fp->mode != FUNC_PARAM_OUT) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("variadic argument isn't last function's argument"))); + } ! if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC) { if (outCount == 0) /* save first OUT param's type */ *requiredResultType = toid; outCount++; } + if (fp->mode == FUNC_PARAM_VARIADIC) + { + if (varCount++ > 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("function cannot accept two or more variadic arguments"))); + + /* check variadic parameter type */ + switch (toid) + { + case ANYARRAYOID: + case ANYOID: + break; + default: + if (!OidIsValid(get_element_type(toid))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("variadic argument isn't array"))); + } + } + allTypes[i] = ObjectIdGetDatum(toid); paramModes[i] = CharGetDatum(fp->mode); *************** *** 252,258 **** /* Now construct the proper outputs as needed */ *parameterTypes = buildoidvector(inTypes, inCount); ! if (outCount > 0) { *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID, sizeof(Oid), true, 'i'); --- 281,287 ---- /* Now construct the proper outputs as needed */ *parameterTypes = buildoidvector(inTypes, inCount); ! if (outCount > 0 || varCount > 0) { *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID, sizeof(Oid), true, 'i'); *** ./src/backend/parser/gram.y.orig 2008-06-24 10:30:01.000000000 +0200 --- ./src/backend/parser/gram.y 2008-06-24 10:32:36.000000000 +0200 *************** *** 444,450 **** UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UPDATE USER USING ! VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING VERBOSE VERSION_P VIEW VOLATILE WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE --- 444,450 ---- UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UPDATE USER USING ! VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VERBOSE VERSION_P VIEW VOLATILE WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE *************** *** 4204,4209 **** --- 4204,4210 ---- | OUT_P { $$ = FUNC_PARAM_OUT; } | INOUT { $$ = FUNC_PARAM_INOUT; } | IN_P OUT_P { $$ = FUNC_PARAM_INOUT; } + | VARIADIC { $$ = FUNC_PARAM_VARIADIC; } ; /* *** ./src/backend/parser/keywords.c.orig 2008-06-24 10:29:58.000000000 +0200 --- ./src/backend/parser/keywords.c 2008-06-24 10:30:50.000000000 +0200 *************** *** 393,398 **** --- 393,399 ---- {"value", VALUE_P, UNRESERVED_KEYWORD}, {"values", VALUES, COL_NAME_KEYWORD}, {"varchar", VARCHAR, COL_NAME_KEYWORD}, + {"variadic", VARIADIC, UNRESERVED_KEYWORD}, {"varying", VARYING, UNRESERVED_KEYWORD}, {"verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD}, {"version", VERSION_P, UNRESERVED_KEYWORD}, *** ./src/backend/parser/parse_func.c.orig 2008-06-24 13:09:17.000000000 +0200 --- ./src/backend/parser/parse_func.c 2008-06-24 15:58:44.000000000 +0200 *************** *** 76,81 **** --- 76,83 ---- Node *retval; bool retset; FuncDetailCode fdresult; + int nvargs; + Oid va_oid; /* * Most of the rest of the parser just assumes that functions do not have *************** *** 158,164 **** */ fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types, &funcid, &rettype, &retset, ! &declared_arg_types); if (fdresult == FUNCDETAIL_COERCION) { /* --- 160,167 ---- */ fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types, &funcid, &rettype, &retset, ! &declared_arg_types, ! &nvargs, &va_oid); if (fdresult == FUNCDETAIL_COERCION) { /* *************** *** 229,234 **** --- 232,279 ---- } /* + * Last nvargs arguments are transformed to array when function is + * non "any" variadic. + */ + if (nvargs> 0 && va_oid != ANYOID) + { + A_ArrayExpr *n = makeNode(A_ArrayExpr); + Node *tn; + + if (nvargs < nargs) + { + int non_var_args = nargs - nvargs; + List *vargs; + + vargs = list_copy_tail(fargs, non_var_args); + fargs = list_truncate(fargs, non_var_args); + n->elements = vargs; + tn = transformExpr(pstate, (Node *) n); + fargs = lappend(fargs, tn); + } + else + { + /* array from all argumenst */ + n->elements = fargs; + tn= transformExpr(pstate, (Node *) n); + fargs = list_make1(tn); + } + + /* + * Now we have to correct argument's metadata used in + * enforce_generic_type_consistency and make_fn_arguments. These + * functions needs actual values of nargs, actual_arg_types and + * real_arg_types. + */ + nargs = nargs - nvargs + 1; + actual_arg_types[nargs - 1] = ((ArrayExpr *) tn)->array_typeid; + if (va_oid != ANYELEMENTOID) + declared_arg_types[nargs - 1] = get_array_type(va_oid); + else + declared_arg_types[nargs - 1] = ANYARRAYOID; + } + + /* * enforce consistency with polymorphic argument and return types, * possibly adjusting return type or declared_arg_types (which will be * used as the cast destination by make_fn_arguments) *************** *** 697,709 **** Oid *funcid, /* return value */ Oid *rettype, /* return value */ bool *retset, /* return value */ ! Oid **true_typeids) /* return value */ { FuncCandidateList raw_candidates; FuncCandidateList best_candidate; /* Get list of possible candidates from namespace search */ ! raw_candidates = FuncnameGetCandidates(funcname, nargs); /* * Quickly check if there is an exact match to the input datatypes (there --- 742,756 ---- Oid *funcid, /* return value */ Oid *rettype, /* return value */ bool *retset, /* return value */ ! Oid **true_typeids, /* return value */ ! int *nvargs, /* return value */ ! Oid *va_oid) /* return value */ { FuncCandidateList raw_candidates; FuncCandidateList best_candidate; /* Get list of possible candidates from namespace search */ ! raw_candidates = FuncnameGetCandidates(funcname, nargs, true); /* * Quickly check if there is an exact match to the input datatypes (there *************** *** 787,792 **** --- 834,841 ---- *rettype = targetType; *retset = false; *true_typeids = argtypes; + *nvargs = 0; + *va_oid = InvalidOid; return FUNCDETAIL_COERCION; } } *************** *** 836,841 **** --- 885,892 ---- *funcid = best_candidate->oid; *true_typeids = best_candidate->args; + *nvargs = best_candidate->nvargs; + *va_oid = best_candidate->variadic_oid; ftup = SearchSysCache(PROCOID, ObjectIdGetDatum(best_candidate->oid), *************** *** 1189,1195 **** { FuncCandidateList clist; ! clist = FuncnameGetCandidates(funcname, nargs); while (clist) { --- 1240,1246 ---- { FuncCandidateList clist; ! clist = FuncnameGetCandidates(funcname, nargs, false); while (clist) { *** ./src/backend/utils/adt/regproc.c.orig 2008-06-24 13:12:03.000000000 +0200 --- ./src/backend/utils/adt/regproc.c 2008-06-24 13:12:53.000000000 +0200 *************** *** 131,137 **** * pg_proc entries in the current search path. */ names = stringToQualifiedNameList(pro_name_or_oid); ! clist = FuncnameGetCandidates(names, -1); 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, false); if (clist == NULL) ereport(ERROR, *************** *** 189,195 **** * Would this proc be found (uniquely!) by regprocin? If not, * qualify it. */ ! clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1); if (clist != NULL && clist->next == NULL && clist->oid == proid) nspname = NULL; --- 189,195 ---- * Would this proc be found (uniquely!) by regprocin? If not, * qualify it. */ ! clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1, false); if (clist != NULL && clist->next == NULL && clist->oid == proid) nspname = NULL; *************** *** 276,282 **** */ parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes); ! clist = FuncnameGetCandidates(names, nargs); for (; clist; clist = clist->next) { --- 276,282 ---- */ parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes); ! clist = FuncnameGetCandidates(names, nargs, false); for (; clist; clist = clist->next) { *** ./src/backend/utils/adt/ruleutils.c.orig 2008-06-24 14:37:19.000000000 +0200 --- ./src/backend/utils/adt/ruleutils.c 2008-06-24 14:38:28.000000000 +0200 *************** *** 5344,5349 **** --- 5344,5351 ---- Oid p_rettype; bool p_retset; Oid *p_true_typeids; + int nvargs; + Oid va_oid; proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), *************** *** 5362,5368 **** p_result = func_get_detail(list_make1(makeString(proname)), NIL, nargs, argtypes, &p_funcid, &p_rettype, ! &p_retset, &p_true_typeids); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) && p_funcid == funcid) nspname = NULL; --- 5364,5371 ---- p_result = func_get_detail(list_make1(makeString(proname)), NIL, nargs, argtypes, &p_funcid, &p_rettype, ! &p_retset, &p_true_typeids, ! &nvargs, &va_oid); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) && p_funcid == funcid) nspname = NULL; *** ./src/backend/utils/fmgr/funcapi.c.orig 2008-06-24 16:18:51.000000000 +0200 --- ./src/backend/utils/fmgr/funcapi.c 2008-06-24 16:21:54.000000000 +0200 *************** *** 844,850 **** numoutargs = 0; for (i = 0; i < numargs; i++) { ! if (argmodes[i] == PROARGMODE_IN) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT); --- 844,850 ---- numoutargs = 0; for (i = 0; i < numargs; i++) { ! if (argmodes[i] == PROARGMODE_IN || argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT); *************** *** 994,1000 **** { char *pname; ! if (argmodes[i] == PROARGMODE_IN) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT); --- 994,1000 ---- { char *pname; ! if (argmodes[i] == PROARGMODE_IN || argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT); *** ./src/bin/pg_dump/pg_dump.c.orig 2008-06-24 15:05:21.000000000 +0200 --- ./src/bin/pg_dump/pg_dump.c 2008-06-24 15:09:08.000000000 +0200 *************** *** 6435,6449 **** { switch (argmodes[j][0]) { ! case 'i': argmode = ""; break; ! case 'o': argmode = "OUT "; break; ! case 'b': argmode = "INOUT "; break; default: write_msg(NULL, "WARNING: bogus value in proargmodes array\n"); argmode = ""; --- 6435,6452 ---- { switch (argmodes[j][0]) { ! case PROARGMODE_IN: argmode = ""; break; ! case PROARGMODE_OUT: argmode = "OUT "; break; ! case PROARGMODE_INOUT: argmode = "INOUT "; break; + case PROARGMODE_VARIADIC: + argmode = "VARIADIC "; + break; default: write_msg(NULL, "WARNING: bogus value in proargmodes array\n"); argmode = ""; *** ./src/bin/psql/describe.c.orig 2008-06-24 15:17:23.000000000 +0200 --- ./src/bin/psql/describe.c 2008-06-24 15:18:26.000000000 +0200 *************** *** 187,192 **** --- 187,193 ---- " WHEN p.proargmodes[s.i] = 'i' THEN ''\n" " WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n" " WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n" + " WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n" " END ||\n" " CASE\n" " WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n" *** ./src/include/catalog/namespace.h.orig 2008-06-24 12:48:23.000000000 +0200 --- ./src/include/catalog/namespace.h 2008-06-24 12:59:36.000000000 +0200 *************** *** 29,34 **** --- 29,36 ---- int pathpos; /* for internal use of namespace lookup */ Oid oid; /* the function or operator's OID */ int nargs; /* number of arg types returned */ + int nvargs; /* number of variadic arguments */ + Oid variadic_oid; /* Oid of variadic argument */ Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */ } *FuncCandidateList; /* VARIABLE LENGTH STRUCT */ *************** *** 51,57 **** extern Oid TypenameGetTypid(const char *typname); extern bool TypeIsVisible(Oid typid); ! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs); extern bool FunctionIsVisible(Oid funcid); extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright); --- 53,60 ---- extern Oid TypenameGetTypid(const char *typname); extern bool TypeIsVisible(Oid typid); ! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs, ! bool transform_variadic); extern bool FunctionIsVisible(Oid funcid); extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright); *** ./src/include/catalog/pg_proc.h.orig 2008-06-24 13:33:45.000000000 +0200 --- ./src/include/catalog/pg_proc.h 2008-06-24 13:33:24.000000000 +0200 *************** *** 4466,4470 **** --- 4466,4471 ---- #define PROARGMODE_IN 'i' #define PROARGMODE_OUT 'o' #define PROARGMODE_INOUT 'b' + #define PROARGMODE_VARIADIC 'v' #endif /* PG_PROC_H */ *** ./src/include/nodes/parsenodes.h.orig 2008-06-24 10:35:02.000000000 +0200 --- ./src/include/nodes/parsenodes.h 2008-06-24 10:35:09.000000000 +0200 *************** *** 1568,1574 **** /* the assigned enum values appear in pg_proc, don't change 'em! */ FUNC_PARAM_IN = 'i', /* input only */ FUNC_PARAM_OUT = 'o', /* output only */ ! FUNC_PARAM_INOUT = 'b' /* both */ } FunctionParameterMode; typedef struct FunctionParameter --- 1568,1575 ---- /* the assigned enum values appear in pg_proc, don't change 'em! */ FUNC_PARAM_IN = 'i', /* input only */ FUNC_PARAM_OUT = 'o', /* output only */ ! FUNC_PARAM_INOUT = 'b', /* both */ ! FUNC_PARAM_VARIADIC = 'v' /* variadic */ } FunctionParameterMode; typedef struct FunctionParameter *** ./src/include/parser/parse_func.h.orig 2008-06-24 14:16:40.000000000 +0200 --- ./src/include/parser/parse_func.h 2008-06-24 14:17:13.000000000 +0200 *************** *** 49,55 **** extern FuncDetailCode func_get_detail(List *funcname, List *fargs, int nargs, Oid *argtypes, Oid *funcid, Oid *rettype, ! bool *retset, Oid **true_typeids); extern int func_match_argtypes(int nargs, Oid *input_typeids, --- 49,56 ---- extern FuncDetailCode func_get_detail(List *funcname, List *fargs, int nargs, Oid *argtypes, Oid *funcid, Oid *rettype, ! bool *retset, Oid **true_typeids, ! int *nvargs, Oid *va_oid); extern int func_match_argtypes(int nargs, Oid *input_typeids, *** ./src/interfaces/ecpg/preproc/preproc.y.orig 2008-06-24 11:12:00.000000000 +0200 --- ./src/interfaces/ecpg/preproc/preproc.y 2008-06-24 11:13:52.000000000 +0200 *************** *** 489,495 **** UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UPDATE USER USING ! VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING VERBOSE VERSION_P VIEW VOLATILE WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE --- 489,495 ---- UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UPDATE USER USING ! VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VERBOSE VERSION_P VIEW VOLATILE WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE *************** *** 6668,6673 **** --- 6668,6674 ---- | VALIDATOR { $$ = make_str("validator"); } | VALUE_P { $$ = make_str("value"); } | VARYING { $$ = make_str("varying"); } + | VARIADIC { $$ = make_str("variadic"); } | VERSION_P { $$ = make_str("version"); } | VIEW { $$ = make_str("view"); } | VOLATILE { $$ = make_str("volatile"); } *** ./src/pl/plpgsql/src/pl_comp.c.orig 2008-06-24 16:23:47.000000000 +0200 --- ./src/pl/plpgsql/src/pl_comp.c 2008-06-24 16:25:53.000000000 +0200 *************** *** 426,432 **** { argitemtype = PLPGSQL_NSTYPE_VAR; /* input argument vars are forced to be CONSTANT */ ! if (argmode == PROARGMODE_IN) ((PLpgSQL_var *) argvariable)->isconst = true; } else --- 426,432 ---- { argitemtype = PLPGSQL_NSTYPE_VAR; /* input argument vars are forced to be CONSTANT */ ! if (argmode == PROARGMODE_IN || argmode == PROARGMODE_VARIADIC) ((PLpgSQL_var *) argvariable)->isconst = true; } else *************** *** 436,442 **** } /* Remember arguments in appropriate arrays */ ! if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT) in_arg_varnos[num_in_args++] = argvariable->dno; if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT) out_arg_variables[num_out_args++] = argvariable; --- 436,443 ---- } /* Remember arguments in appropriate arrays */ ! if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT ! || argmode == PROARGMODE_VARIADIC) in_arg_varnos[num_in_args++] = argvariable->dno; if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT) out_arg_variables[num_out_args++] = argvariable; *** ./src/test/regress/expected/plpgsql.out.orig 2008-06-24 16:33:20.000000000 +0200 --- ./src/test/regress/expected/plpgsql.out 2008-06-24 16:32:32.000000000 +0200 *************** *** 3544,3546 **** --- 3544,3611 ---- drop function catch(); drop function case_test(bigint); + -- variadic fuction test + create or replace function vari(variadic int[]) + returns void as $$ + begin + for i in array_lower($1,1)..array_upper($1,1) loop + raise notice '%', $1[i]; + end loop; end; + $$ language plpgsql; + select vari(1,2,3,4,5); + NOTICE: 1 + NOTICE: 2 + NOTICE: 3 + NOTICE: 4 + NOTICE: 5 + vari + ------ + + (1 row) + + select vari(3,4,5); + NOTICE: 3 + NOTICE: 4 + NOTICE: 5 + vari + ------ + + (1 row) + + drop function vari(int[]); + -- coerce test + create or replace function pleast(variadic numeric[]) + returns numeric as $$ + declare aux numeric = $1[array_lower($1,1)]; + begin + for i in array_lower($1,1)+1..array_upper($1,1) loop + if $1[i] < aux then aux := $1[i]; end if; + end loop; + return aux; + end; + $$ language plpgsql immutable strict; + select pleast(10,1,2,3,-16); + pleast + -------- + -16 + (1 row) + + select pleast(10.2,2.2,-1.1); + pleast + -------- + -1.1 + (1 row) + + select pleast(10.2,10, -20); + pleast + -------- + -20 + (1 row) + + select pleast(10,20, -1.0); + pleast + -------- + -1.0 + (1 row) + + drop function pleast(numeric[]); *** ./src/test/regress/expected/polymorphism.out.orig 2008-06-24 16:10:52.000000000 +0200 --- ./src/test/regress/expected/polymorphism.out 2008-06-24 16:07:12.000000000 +0200 *************** *** 613,615 **** --- 613,658 ---- SFUNC = add_group, STYPE = int8[] ); + --test for variadic polymorphic function + create function myleast(variadic anyarray) + returns anyelement as $$ + select min($1[i]) + from generate_subscripts($1,1) g(i) + $$ language sql immutable strict; + select myleast(10, 1, 20, 33); + myleast + --------- + 1 + (1 row) + + select myleast(1.1, 0.22, 0.55); + myleast + --------- + 0.22 + (1 row) + + -- visibility test + select pg_catalog.pg_function_is_visible('myleast(anyarray)'::regprocedure::int); + pg_function_is_visible + ------------------------ + t + (1 row) + + drop function myleast(anyarray); + create or replace function concat(varchar, variadic anyarray) + returns varchar as $$ + select array_to_string($2, $1); + $$ language sql immutable strict; + select concat('%', 1, 2, 3, 4, 5); + concat + ----------- + 1%2%3%4%5 + (1 row) + + select concat('|', 'a'::text, 'b', 'c'); + concat + -------- + a|b|c + (1 row) + + drop function concat(varchar, anyarray); *** ./src/test/regress/sql/plpgsql.sql.orig 2008-06-24 16:01:55.000000000 +0200 --- ./src/test/regress/sql/plpgsql.sql 2008-06-24 16:30:50.000000000 +0200 *************** *** 2878,2880 **** --- 2878,2913 ---- drop function catch(); drop function case_test(bigint); + + -- variadic fuction test + create or replace function vari(variadic int[]) + returns void as $$ + begin + for i in array_lower($1,1)..array_upper($1,1) loop + raise notice '%', $1[i]; + end loop; end; + $$ language plpgsql; + + select vari(1,2,3,4,5); + select vari(3,4,5); + + drop function vari(int[]); + + -- coerce test + create or replace function pleast(variadic numeric[]) + returns numeric as $$ + declare aux numeric = $1[array_lower($1,1)]; + begin + for i in array_lower($1,1)+1..array_upper($1,1) loop + if $1[i] < aux then aux := $1[i]; end if; + end loop; + return aux; + end; + $$ language plpgsql immutable strict; + + select pleast(10,1,2,3,-16); + select pleast(10.2,2.2,-1.1); + select pleast(10.2,10, -20); + select pleast(10,20, -1.0); + + drop function pleast(numeric[]); *** ./src/test/regress/sql/polymorphism.sql.orig 2008-06-24 16:01:52.000000000 +0200 --- ./src/test/regress/sql/polymorphism.sql 2008-06-24 16:04:16.000000000 +0200 *************** *** 426,428 **** --- 426,453 ---- SFUNC = add_group, STYPE = int8[] ); + + --test for variadic polymorphic function + create function myleast(variadic anyarray) + returns anyelement as $$ + select min($1[i]) + from generate_subscripts($1,1) g(i) + $$ language sql immutable strict; + + select myleast(10, 1, 20, 33); + select myleast(1.1, 0.22, 0.55); + -- visibility test + select pg_catalog.pg_function_is_visible('myleast(anyarray)'::regprocedure::int); + + drop function myleast(anyarray); + + create or replace function concat(varchar, variadic anyarray) + returns varchar as $$ + select array_to_string($2, $1); + $$ language sql immutable strict; + + select concat('%', 1, 2, 3, 4, 5); + select concat('|', 'a'::text, 'b', 'c'); + + drop function concat(varchar, anyarray); +
-- Sent via pgsql-patches mailing list (pgsql-patches@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-patches