*** ./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 **** The mode of an argument: either IN, OUT, ! or INOUT. If omitted, the default is IN. --- 102,109 ---- The mode of an argument: either IN, OUT, ! INOUT or VARIADIC. If omitted, ! the default is IN. *** ./doc/src/sgml/xfunc.sgml.orig 2008-06-24 16:53:58.000000000 +0200 --- ./doc/src/sgml/xfunc.sgml 2008-06-26 13:34:20.000000000 +0200 *************** *** 578,584 **** Parameters can be marked as IN (the default), ! OUT, or INOUT. An INOUT parameter serves as both an input parameter (part of the calling argument list) and an output parameter (part of the result record type). --- 578,585 ---- Parameters can be marked as IN (the default), ! OUT, INOUT, or VARIADIC. ! An INOUT parameter serves as both an input parameter (part of the calling argument list) and an output parameter (part of the result record type). *************** *** 805,810 **** --- 806,833 ---- + + + Variadic <acronym>SQL</acronym> Functions + + + SQL functions can be declared to accept + variable number of arguments. + + CREATE FUNCTION mleast(variadic args 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) + + + + *** ./src/backend/catalog/namespace.c.orig 2008-06-24 11:24:34.000000000 +0200 --- ./src/backend/catalog/namespace.c 2008-06-26 16:41:06.000000000 +0200 *************** *** 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,645 ---- 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; + va_oid = get_variadic_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; } --- 722,762 ---- /* * Okay to add it to result list */ ! if (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; } *************** *** 759,771 **** for (; clist; clist = clist->next) { ! if (memcmp(clist->args, procform->proargtypes.values, ! nargs * sizeof(Oid)) == 0) { ! /* Found the expected entry; is it the right proc? */ ! visible = (clist->oid == funcid); ! break; ! } } } --- 815,848 ---- for (; clist; clist = clist->next) { ! /* ! * Oid of expanded variadic argument is different than record ! * in proargtypes.values. ! */ ! if (clist->nvargs > 0) { ! Oid va_oid; ! ! if (memcmp(clist->args, procform->proargtypes.values, ! (nargs - 1) * sizeof(Oid)) != 0) ! continue; ! va_oid = get_variadic_element_type( ! procform->proargtypes.values[nargs - 1]); ! ! if (clist->args[nargs - 1] == va_oid) ! { ! visible = (clist->oid == funcid); ! break; ! } ! } ! else ! if (memcmp(clist->args, procform->proargtypes.values, ! nargs * sizeof(Oid)) == 0) ! { ! /* Found the expected entry; is it the right proc? */ ! visible = (clist->oid == funcid); ! break; ! } } } *** ./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-25 16:30:29.000000000 +0200 *************** *** 173,178 **** --- 173,179 ---- Datum *paramNames; int outCount = 0; bool have_names = false; + int varCount = 0; ListCell *x; int i; *************** *** 227,235 **** 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; --- 228,270 ---- errmsg("functions cannot accept set arguments"))); if (fp->mode != FUNC_PARAM_OUT) + { + 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"))); + /* + * type of declared variadic variable is cecked and changed to + * array. + */ + switch (toid) + { + case ANYOID: + break; + case ANYELEMENTOID: + toid = ANYARRAYOID; + break; + default: + toid = get_array_type(toid); + if (!OidIsValid(toid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("variadic argument isn't array"))); + } + } + else + /* 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"))); + inTypes[inCount++] = toid; + } ! if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC) { if (outCount == 0) /* save first OUT param's type */ *requiredResultType = toid; *************** *** 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'); --- 287,293 ---- /* 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-25 18:03:20.000000000 +0200 *************** *** 53,58 **** --- 53,59 ---- #include "catalog/index.h" #include "catalog/namespace.h" + #include "catalog/pg_type.h" #include "commands/defrem.h" #include "nodes/makefuncs.h" #include "parser/gramparse.h" *************** *** 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 --- 445,451 ---- 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 **** --- 4205,4211 ---- | 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-26 14:47:10.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,703 **** Oid *funcid, /* return value */ Oid *rettype, /* return value */ bool *retset, /* return value */ ! Oid **true_typeids) /* return value */ { FuncCandidateList raw_candidates; FuncCandidateList best_candidate; --- 742,750 ---- 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; *************** *** 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), *************** *** 1190,1200 **** FuncCandidateList clist; clist = FuncnameGetCandidates(funcname, nargs); - while (clist) { if (memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0) return clist->oid; clist = clist->next; } --- 1241,1251 ---- FuncCandidateList clist; clist = FuncnameGetCandidates(funcname, nargs); while (clist) { if (memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0) return clist->oid; + clist = clist->next; } *** ./src/backend/utils/adt/oid.c.orig 2008-06-25 15:22:20.000000000 +0200 --- ./src/backend/utils/adt/oid.c 2008-06-26 16:19:41.000000000 +0200 *************** *** 21,27 **** #include "libpq/pqformat.h" #include "utils/array.h" #include "utils/builtins.h" ! #define OidVectorSize(n) (offsetof(oidvector, values) + (n) * sizeof(Oid)) --- 21,27 ---- #include "libpq/pqformat.h" #include "utils/array.h" #include "utils/builtins.h" ! #include "utils/lsyscache.h" #define OidVectorSize(n) (offsetof(oidvector, values) + (n) * sizeof(Oid)) *************** *** 419,421 **** --- 419,451 ---- PG_RETURN_BOOL(cmp > 0); } + + /* + * pg_get_element_type function is used for overwriting real variadic type + */ + Oid + get_variadic_element_type(Oid oid) + { + Oid result; + + switch (oid) + { + case ANYOID: + result = ANYOID; + break; + case ANYARRAYOID: + result = ANYELEMENTOID; + break; + default: + result = get_element_type(oid); + } + return result; + } + + Datum + pg_get_element_type(PG_FUNCTION_ARGS) + { + Oid oid = PG_GETARG_OID(0); + + PG_RETURN_OID(get_variadic_element_type(oid)); + } *** ./src/backend/utils/adt/regproc.c.orig 2008-06-24 13:12:03.000000000 +0200 --- ./src/backend/utils/adt/regproc.c 2008-06-26 16:32:54.000000000 +0200 *************** *** 38,43 **** --- 38,44 ---- #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/tqual.h" + #include "utils/array.h" static void parseNameAndArgTypes(const char *string, bool allowNone, List **names, int *nargs, Oid *argtypes); *************** *** 318,325 **** --- 319,347 ---- int i; char *nspname; StringInfoData buf; + Datum proargmodes; + bool isnull; + bool variadic = false; /* XXX no support here for bootstrap mode */ + + /* + * variadic function detection + */ + proargmodes = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargmodes, &isnull); + if (!isnull) + { + ArrayType *ar = DatumGetArrayTypeP(proargmodes); + char *argmodes; + + argmodes = ARR_DATA_PTR(ar); + for (i = 0; i < ARR_DIMS(ar)[0]; i++) + if (argmodes[i] == PROARGMODE_VARIADIC) + { + variadic = true; + break; + } + } initStringInfo(&buf); *************** *** 340,345 **** --- 362,374 ---- if (i > 0) appendStringInfoChar(&buf, ','); + /* last variadic argument */ + if (variadic && i == (nargs - 1)) + { + appendStringInfoString(&buf, "VARIADIC "); + thisargtype = get_variadic_element_type(thisargtype); + } + appendStringInfoString(&buf, format_type_be(thisargtype)); } appendStringInfoChar(&buf, ')'); *** ./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-25 17:15:08.000000000 +0200 *************** *** 6429,6449 **** const char *argname; typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j]; ! typname = getFormattedTypeName(typid, zeroAsOpaque); if (argmodes) { 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 = ""; --- 6429,6452 ---- const char *argname; typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j]; ! typname = getFormattedTypeName(typid, zeroAsOpaque | (argmodes[j][0] == PROARGMODE_VARIADIC)? variadicArgument : 0); if (argmodes) { 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 = ""; *************** *** 10260,10266 **** } query = createPQExpBuffer(); ! if (g_fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)", oid); --- 10263,10274 ---- } query = createPQExpBuffer(); ! if ((opts & variadicArgument) != 0) ! { ! appendPQExpBuffer(query, "SELECT pg_catalog.format_type(pg_catalog.pg_get_element_type('%u'::pg_catalog.oid), NULL)", ! oid); ! } ! else if (g_fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)", oid); *** ./src/bin/pg_dump/pg_dump.h.orig 2008-06-25 17:07:25.000000000 +0200 --- ./src/bin/pg_dump/pg_dump.h 2008-06-25 17:13:18.000000000 +0200 *************** *** 436,442 **** zeroAsOpaque = 1, zeroAsAny = 2, zeroAsStar = 4, ! zeroAsNone = 8 } OidOptions; extern void AssignDumpId(DumpableObject *dobj); --- 436,443 ---- zeroAsOpaque = 1, zeroAsAny = 2, zeroAsStar = 4, ! zeroAsNone = 8, ! variadicArgument = 16, } OidOptions; extern void AssignDumpId(DumpableObject *dobj); *** ./src/bin/psql/describe.c.orig 2008-06-24 15:17:23.000000000 +0200 --- ./src/bin/psql/describe.c 2008-06-25 16:41:17.000000000 +0200 *************** *** 187,198 **** " 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" " END ||\n" " CASE\n" " WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n" " ELSE p.proargnames[s.i] || ' ' \n" " END ||\n" ! " pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n" " FROM\n" " pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n" " ), ', ')\n" --- 187,202 ---- " 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" " ELSE p.proargnames[s.i] || ' ' \n" " END ||\n" ! " pg_catalog.format_type(CASE\n" ! " WHEN p.proargmodes[s.i] = 'v' THEN pg_catalog.pg_get_element_type(p.proallargtypes[s.i])\n" ! " ELSE p.proallargtypes[s.i]\n" ! " END, NULL)\n" " FROM\n" " pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n" " ), ', ')\n" *** ./src/include/catalog/namespace.h.orig 2008-06-24 12:48:23.000000000 +0200 --- ./src/include/catalog/namespace.h 2008-06-26 14:45:13.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 */ *** ./src/include/catalog/pg_proc.h.orig 2008-06-24 13:33:45.000000000 +0200 --- ./src/include/catalog/pg_proc.h 2008-06-25 16:10:41.000000000 +0200 *************** *** 2660,2665 **** --- 2660,2668 ---- DATA(insert OID = 1799 ( oidout PGNSP PGUID 12 1 0 f f t f i 1 2275 "26" _null_ _null_ _null_ oidout - _null_ _null_ )); DESCR("I/O"); + DATA(insert OID = 2162 ( pg_get_element_type PGNSP PGUID 12 1 0 f f t f i 1 26 "26" _null_ _null_ _null_ pg_get_element_type - _null_ _null_ )); + DESCR("returns element type of array"); + DATA(insert OID = 1810 ( bit_length PGNSP PGUID 14 1 0 f f t f i 1 23 "17" _null_ _null_ _null_ "select pg_catalog.octet_length($1) * 8" - _null_ _null_ )); DESCR("length in bits"); *************** *** 4466,4470 **** --- 4469,4474 ---- #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/include/utils/builtins.h.orig 2008-06-25 15:33:30.000000000 +0200 --- ./src/include/utils/builtins.h 2008-06-26 16:16:09.000000000 +0200 *************** *** 436,441 **** --- 436,443 ---- extern Datum oidvectorge(PG_FUNCTION_ARGS); extern Datum oidvectorgt(PG_FUNCTION_ARGS); extern oidvector *buildoidvector(const Oid *oids, int n); + extern Datum pg_get_element_type(PG_FUNCTION_ARGS); + extern Oid get_variadic_element_type(Oid oid); /* pseudotypes.c */ extern Datum cstring_in(PG_FUNCTION_ARGS); *** ./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-26 16:45:05.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(variadic 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(variadic numeric); *** ./src/test/regress/expected/polymorphism.out.orig 2008-06-24 16:10:52.000000000 +0200 --- ./src/test/regress/expected/polymorphism.out 2008-06-26 16:51:20.000000000 +0200 *************** *** 613,615 **** --- 613,658 ---- SFUNC = add_group, STYPE = int8[] ); + --test for variadic polymorphic function + create function myleast(variadic anyelement) + 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(anyelement)'::regprocedure::int); + pg_function_is_visible + ------------------------ + t + (1 row) + + drop function myleast(anyelement); + create or replace function concat(varchar, variadic anyelement) + 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, variadic anyelement); *** ./src/test/regress/sql/plpgsql.sql.orig 2008-06-24 16:01:55.000000000 +0200 --- ./src/test/regress/sql/plpgsql.sql 2008-06-25 17:23:16.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(variadic 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(variadic numeric); *** ./src/test/regress/sql/polymorphism.sql.orig 2008-06-24 16:01:52.000000000 +0200 --- ./src/test/regress/sql/polymorphism.sql 2008-06-26 16:50:09.000000000 +0200 *************** *** 426,428 **** --- 426,453 ---- SFUNC = add_group, STYPE = int8[] ); + + --test for variadic polymorphic function + create function myleast(variadic anyelement) + 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(anyelement)'::regprocedure::int); + + drop function myleast(anyelement); + + create or replace function concat(varchar, variadic anyelement) + 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, variadic anyelement); +