*** a/src/backend/catalog/pg_aggregate.c --- b/src/backend/catalog/pg_aggregate.c *************** *** 332,337 **** lookup_agg_function(List *fnName, --- 332,338 ---- Oid fnOid; bool retset; int nvargs; + Oid vatype; Oid *true_oid_array; FuncDetailCode fdresult; AclResult aclresult; *************** *** 346,352 **** lookup_agg_function(List *fnName, */ fdresult = func_get_detail(fnName, NIL, NIL, nargs, input_types, false, false, ! &fnOid, rettype, &retset, &nvargs, &true_oid_array, NULL); /* only valid case is a normal function not returning a set */ --- 347,354 ---- */ fdresult = func_get_detail(fnName, NIL, NIL, nargs, input_types, false, false, ! &fnOid, rettype, &retset, ! &nvargs, &vatype, &true_oid_array, NULL); /* only valid case is a normal function not returning a set */ *** a/src/backend/parser/parse_func.c --- b/src/backend/parser/parse_func.c *************** *** 79,84 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 79,85 ---- Node *retval; bool retset; int nvargs; + Oid vatype; FuncDetailCode fdresult; /* *************** *** 214,220 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, fdresult = func_get_detail(funcname, fargs, argnames, nargs, actual_arg_types, !func_variadic, true, ! &funcid, &rettype, &retset, &nvargs, &declared_arg_types, &argdefaults); if (fdresult == FUNCDETAIL_COERCION) { --- 215,222 ---- fdresult = func_get_detail(funcname, fargs, argnames, nargs, actual_arg_types, !func_variadic, true, ! &funcid, &rettype, &retset, ! &nvargs, &vatype, &declared_arg_types, &argdefaults); if (fdresult == FUNCDETAIL_COERCION) { *************** *** 376,381 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 378,399 ---- fargs = lappend(fargs, newa); } + /* + * When function is called with VARIADIC labeled parameter, + * and declared_arg_type is "ANY", then sanitize a real parameter + * type now - should be an array. + */ + if (nargs > 0 && vatype == ANYOID && func_variadic) + { + Oid va_arr_typid = actual_arg_types[nargs - 1]; + + if (!OidIsValid(get_element_type(va_arr_typid))) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("VARIADIC argument must be an array"), + parser_errposition(pstate, exprLocation((Node *) llast(fargs))))); + } + /* build the appropriate output structure */ if (fdresult == FUNCDETAIL_NORMAL) { *************** *** 1015,1020 **** func_get_detail(List *funcname, --- 1033,1039 ---- Oid *rettype, /* return value */ bool *retset, /* return value */ int *nvargs, /* return value */ + Oid *vatype, /* return value */ Oid **true_typeids, /* return value */ List **argdefaults) /* optional return value */ { *************** *** 1233,1238 **** func_get_detail(List *funcname, --- 1252,1258 ---- pform = (Form_pg_proc) GETSTRUCT(ftup); *rettype = pform->prorettype; *retset = pform->proretset; + *vatype = pform->provariadic; /* fetch default args if caller wants 'em */ if (argdefaults && best_candidate->ndargs > 0) { *** a/src/backend/utils/adt/ruleutils.c --- b/src/backend/utils/adt/ruleutils.c *************** *** 8550,8555 **** generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, --- 8550,8556 ---- Oid p_rettype; bool p_retset; int p_nvargs; + Oid p_vatype; Oid *p_true_typeids; proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); *************** *** 8600,8606 **** generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, NIL, argnames, nargs, argtypes, !use_variadic, true, &p_funcid, &p_rettype, ! &p_retset, &p_nvargs, &p_true_typeids, NULL); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE || p_result == FUNCDETAIL_WINDOWFUNC) && --- 8601,8608 ---- NIL, argnames, nargs, argtypes, !use_variadic, true, &p_funcid, &p_rettype, ! &p_retset, &p_nvargs, &p_vatype, ! &p_true_typeids, NULL); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE || p_result == FUNCDETAIL_WINDOWFUNC) && *** a/src/backend/utils/adt/varlena.c --- b/src/backend/utils/adt/varlena.c *************** *** 3811,3817 **** concat_internal(const char *sepstr, int argidx, */ if (get_fn_expr_variadic(fcinfo->flinfo)) { - Oid arr_typid; ArrayType *arr; /* Should have just the one argument */ --- 3811,3816 ---- *************** *** 3821,3841 **** concat_internal(const char *sepstr, int argidx, if (PG_ARGISNULL(argidx)) return NULL; ! /* ! * Non-null argument had better be an array. The parser doesn't ! * enforce this for VARIADIC ANY functions (maybe it should?), so that ! * check uses ereport not just elog. ! */ ! arr_typid = get_fn_expr_argtype(fcinfo->flinfo, argidx); ! if (!OidIsValid(arr_typid)) ! elog(ERROR, "could not determine data type of concat() input"); ! ! if (!OidIsValid(get_element_type(arr_typid))) ! ereport(ERROR, ! (errcode(ERRCODE_DATATYPE_MISMATCH), ! errmsg("VARIADIC argument must be an array"))); - /* OK, safe to fetch the array value */ arr = PG_GETARG_ARRAYTYPE_P(argidx); /* --- 3820,3828 ---- if (PG_ARGISNULL(argidx)) return NULL; ! /* Non-null argument had better be an array */ ! Assert(OidIsValid(get_element_type(get_fn_expr_argtype(fcinfo->flinfo, argidx)))); arr = PG_GETARG_ARRAYTYPE_P(argidx); /* *************** *** 4024,4030 **** text_format(PG_FUNCTION_ARGS) /* If argument is marked VARIADIC, expand array into elements */ if (get_fn_expr_variadic(fcinfo->flinfo)) { - Oid arr_typid; ArrayType *arr; int16 elmlen; bool elmbyval; --- 4011,4016 ---- *************** *** 4039,4059 **** text_format(PG_FUNCTION_ARGS) nitems = 0; else { ! /* ! * Non-null argument had better be an array. The parser doesn't ! * enforce this for VARIADIC ANY functions (maybe it should?), so ! * that check uses ereport not just elog. ! */ ! arr_typid = get_fn_expr_argtype(fcinfo->flinfo, 1); ! if (!OidIsValid(arr_typid)) ! elog(ERROR, "could not determine data type of format() input"); ! ! if (!OidIsValid(get_element_type(arr_typid))) ! ereport(ERROR, ! (errcode(ERRCODE_DATATYPE_MISMATCH), ! errmsg("VARIADIC argument must be an array"))); - /* OK, safe to fetch the array value */ arr = PG_GETARG_ARRAYTYPE_P(1); /* Get info about array element type */ --- 4025,4033 ---- nitems = 0; else { ! /* Non-null argument had better be an array */ ! Assert(OidIsValid(get_element_type(get_fn_expr_argtype(fcinfo->flinfo, 1)))); arr = PG_GETARG_ARRAYTYPE_P(1); /* Get info about array element type */ *** a/src/include/parser/parse_func.h --- b/src/include/parser/parse_func.h *************** *** 53,60 **** extern FuncDetailCode func_get_detail(List *funcname, int nargs, Oid *argtypes, bool expand_variadic, bool expand_defaults, Oid *funcid, Oid *rettype, ! bool *retset, int *nvargs, Oid **true_typeids, ! List **argdefaults); extern int func_match_argtypes(int nargs, Oid *input_typeids, --- 53,60 ---- int nargs, Oid *argtypes, bool expand_variadic, bool expand_defaults, Oid *funcid, Oid *rettype, ! bool *retset, int *nvargs, Oid *vatype, ! Oid **true_typeids, List **argdefaults); extern int func_match_argtypes(int nargs, Oid *input_typeids, *** a/src/test/regress/expected/text.out --- b/src/test/regress/expected/text.out *************** *** 149,161 **** select concat_ws(',', variadic array[1,2,3]); 1,2,3 (1 row) ! select concat_ws(',', variadic NULL); concat_ws ----------- (1 row) ! select concat(variadic NULL) is NULL; ?column? ---------- t --- 149,161 ---- 1,2,3 (1 row) ! select concat_ws(',', variadic NULL::int[]); concat_ws ----------- (1 row) ! select concat(variadic NULL::int[]) is NULL; ?column? ---------- t *************** *** 170,175 **** select concat(variadic '{}'::int[]) = ''; --- 170,177 ---- --should fail select concat_ws(',', variadic 10); ERROR: VARIADIC argument must be an array + LINE 1: select concat_ws(',', variadic 10); + ^ /* * format */ *************** *** 313,320 **** select format('%2$s, %1$s', variadic array[1, 2]); 2, 1 (1 row) ! -- variadic argument can be NULL, but should not be referenced ! select format('Hello', variadic NULL); format -------- Hello --- 315,322 ---- 2, 1 (1 row) ! -- variadic argument can be array type NULL, but should not be referenced ! select format('Hello', variadic NULL::int[]); format -------- Hello *** a/src/test/regress/sql/text.sql --- b/src/test/regress/sql/text.sql *************** *** 47,54 **** select quote_literal(e'\\'); -- check variadic labeled argument select concat(variadic array[1,2,3]); select concat_ws(',', variadic array[1,2,3]); ! select concat_ws(',', variadic NULL); ! select concat(variadic NULL) is NULL; select concat(variadic '{}'::int[]) = ''; --should fail select concat_ws(',', variadic 10); --- 47,54 ---- -- check variadic labeled argument select concat(variadic array[1,2,3]); select concat_ws(',', variadic array[1,2,3]); ! select concat_ws(',', variadic NULL::int[]); ! select concat(variadic NULL::int[]) is NULL; select concat(variadic '{}'::int[]) = ''; --should fail select concat_ws(',', variadic 10); *************** *** 92,99 **** select format('%s, %s', variadic array[true, false]::text[]); -- check variadic with positional placeholders select format('%2$s, %1$s', variadic array['first', 'second']); select format('%2$s, %1$s', variadic array[1, 2]); ! -- variadic argument can be NULL, but should not be referenced ! select format('Hello', variadic NULL); -- variadic argument allows simulating more than FUNC_MAX_ARGS parameters select format(string_agg('%s',','), variadic array_agg(i)) from generate_series(1,200) g(i); --- 92,99 ---- -- check variadic with positional placeholders select format('%2$s, %1$s', variadic array['first', 'second']); select format('%2$s, %1$s', variadic array[1, 2]); ! -- variadic argument can be array type NULL, but should not be referenced ! select format('Hello', variadic NULL::int[]); -- variadic argument allows simulating more than FUNC_MAX_ARGS parameters select format(string_agg('%s',','), variadic array_agg(i)) from generate_series(1,200) g(i);