*** a/src/backend/catalog/pg_aggregate.c --- b/src/backend/catalog/pg_aggregate.c *************** *** 344,351 **** 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 */ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) --- 344,351 ---- */ fdresult = func_get_detail(fnName, NIL, NIL, nargs, input_types, false, false, ! &fnOid, rettype, &retset, NULL, ! &nvargs, &true_oid_array, NULL); /* only valid case is a normal function not returning a set */ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) *** a/src/backend/executor/execQual.c --- b/src/backend/executor/execQual.c *************** *** 1728,1733 **** restart: --- 1728,1843 ---- } /* + * this is support for variadic function corner case - using variadic argument + * with VARIADIC modifier when variadic "any" function is called. In this case + * we have to create short lived flinfo and fcinfo, because function descriptors + * can be different in any call of variadic function - depends on content of + * variadic array argument. + */ + if (IsA(fcache->xprstate.expr, FuncExpr) && ((FuncExpr *) fcache->xprstate.expr)->merge_vararg) + { + FmgrInfo sfinfo; /* short lived fake finfo */ + FunctionCallInfo scinfo; /* short lived fake fcinfo */ + FuncExpr sfexpr; /* short lived fake fcinfo */ + MemoryContext oldContext; + Oid vararg_type; + Oid elem_type; + bool elem_typbyval; + int16 elem_typlen; + char elem_typalign; + ArrayType *arr; + ArrayIterator iterator; + List *params_exprs; + ListCell *lc; + int n; + Datum value; + bool isnull; + int slice; + FuncExpr *fexpr = (FuncExpr *) fcache->xprstate.expr; + + /* sanity check, a variadic argument should be a array */ + if (fcinfo->argnull[fcinfo->nargs - 1]) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("variadic argument cannot be null"))); + + vararg_type = get_fn_expr_argtype(fcinfo->flinfo, fcinfo->nargs - 1); + if (!OidIsValid(vararg_type)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not determinate variadic argument data type"))); + if (!OidIsValid(get_element_type(vararg_type))) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("variadic argument should be an array"))); + + /* switch to short lived per-tuple context for node's copies */ + oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + arr = DatumGetArrayTypeP(fcinfo->arg[fcinfo->nargs - 1]); + slice = ARR_NDIM(arr) > 1 ? ARR_NDIM(arr) - 1 : 0; + elem_type = slice > 0 ? vararg_type : ARR_ELEMTYPE(arr); + + /* ToDo: use element_type depends for dimensions */ + get_typlenbyvalalign(elem_type, + &elem_typlen, + &elem_typbyval, + &elem_typalign); + + scinfo = (FunctionCallInfo) palloc(sizeof(FunctionCallInfoData)); + + /* create short lived copies of fmgr data */ + fmgr_info_copy(&sfinfo, fcinfo->flinfo, fcinfo->flinfo->fn_mcxt); + memcpy(scinfo, fcinfo, sizeof(FunctionCallInfoData)); + scinfo->flinfo = &sfinfo; + + iterator = array_create_iterator(arr, slice); + + /* + * we have to create short lived fn_expr because variadic + * "any" functions takes types from expression nodes via get_fn_expr_argtype. + */ + n = 0; + params_exprs = NIL; + foreach(lc, fexpr->args) + { + /* don't copy last "variadic argument */ + if (n >= fcinfo->nargs - 1) + break; + + params_exprs = lappend(params_exprs, lc->data.ptr_value); + n++; + } + + while (array_iterate(iterator, &value, &isnull)) + { + /* replace variadic argument */ + scinfo->arg[n] = value; + scinfo->argnull[n] = isnull; + + params_exprs = lappend(params_exprs, makeConst(elem_type, + -1, + scinfo->fncollation, + elem_typlen, + value, + isnull, + elem_typbyval)); + n++; + } + + array_free_iterator(iterator); + + memcpy(&sfexpr, fexpr, sizeof(FuncExpr)); + sfexpr.args = params_exprs; + sfinfo.fn_expr = (fmNodePtr) &sfexpr; + + scinfo->nargs = n; + fcinfo = scinfo; + + MemoryContextSwitchTo(oldContext); + } + + /* * Now call the function, passing the evaluated parameter values. */ if (fcache->func.fn_retset || hasSetArg) *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 1194,1199 **** _copyFuncExpr(const FuncExpr *from) --- 1194,1200 ---- COPY_SCALAR_FIELD(funcid); COPY_SCALAR_FIELD(funcresulttype); COPY_SCALAR_FIELD(funcretset); + COPY_SCALAR_FIELD(merge_vararg); COPY_SCALAR_FIELD(funcformat); COPY_SCALAR_FIELD(funccollid); COPY_SCALAR_FIELD(inputcollid); *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 239,244 **** _equalFuncExpr(const FuncExpr *a, const FuncExpr *b) --- 239,245 ---- COMPARE_SCALAR_FIELD(funcid); COMPARE_SCALAR_FIELD(funcresulttype); COMPARE_SCALAR_FIELD(funcretset); + COMPARE_SCALAR_FIELD(merge_vararg); COMPARE_COERCIONFORM_FIELD(funcformat); COMPARE_SCALAR_FIELD(funccollid); COMPARE_SCALAR_FIELD(inputcollid); *** a/src/backend/nodes/makefuncs.c --- b/src/backend/nodes/makefuncs.c *************** *** 461,466 **** makeFuncExpr(Oid funcid, Oid rettype, List *args, --- 461,467 ---- funcexpr->funcid = funcid; funcexpr->funcresulttype = rettype; funcexpr->funcretset = false; /* only allowed case here */ + funcexpr->merge_vararg = false; /* only allowed case here */ funcexpr->funcformat = fformat; funcexpr->funccollid = funccollid; funcexpr->inputcollid = inputcollid; *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** *** 1000,1005 **** _outFuncExpr(StringInfo str, const FuncExpr *node) --- 1000,1006 ---- WRITE_OID_FIELD(funcid); WRITE_OID_FIELD(funcresulttype); WRITE_BOOL_FIELD(funcretset); + WRITE_BOOL_FIELD(merge_vararg); WRITE_ENUM_FIELD(funcformat, CoercionForm); WRITE_OID_FIELD(funccollid); WRITE_OID_FIELD(inputcollid); *** a/src/backend/nodes/readfuncs.c --- b/src/backend/nodes/readfuncs.c *************** *** 537,542 **** _readFuncExpr(void) --- 537,543 ---- READ_OID_FIELD(funcid); READ_OID_FIELD(funcresulttype); READ_BOOL_FIELD(funcretset); + READ_BOOL_FIELD(merge_vararg); READ_ENUM_FIELD(funcformat, CoercionForm); READ_OID_FIELD(funccollid); READ_OID_FIELD(inputcollid); *** a/src/backend/optimizer/util/clauses.c --- b/src/backend/optimizer/util/clauses.c *************** *** 2330,2335 **** eval_const_expressions_mutator(Node *node, --- 2330,2336 ---- newexpr->funcid = expr->funcid; newexpr->funcresulttype = expr->funcresulttype; newexpr->funcretset = expr->funcretset; + newexpr->merge_vararg = expr->merge_vararg; newexpr->funcformat = expr->funcformat; newexpr->funccollid = expr->funccollid; newexpr->inputcollid = expr->inputcollid; *************** *** 3625,3630 **** simplify_function(Oid funcid, Oid result_type, int32 result_typmod, --- 3626,3632 ---- fexpr.funcid = funcid; fexpr.funcresulttype = result_type; fexpr.funcretset = func_form->proretset; + fexpr.merge_vararg = false; fexpr.funcformat = COERCE_EXPLICIT_CALL; fexpr.funccollid = result_collid; fexpr.inputcollid = input_collid; *************** *** 3959,3964 **** evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, --- 3961,3967 ---- newexpr->funcid = funcid; newexpr->funcresulttype = result_type; newexpr->funcretset = false; + newexpr->merge_vararg = false; newexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */ newexpr->funccollid = result_collid; /* doesn't matter */ newexpr->inputcollid = input_collid; *************** *** 4089,4094 **** inline_function(Oid funcid, Oid result_type, Oid result_collid, --- 4092,4098 ---- fexpr->funcid = funcid; fexpr->funcresulttype = result_type; fexpr->funcretset = false; + fexpr->merge_vararg = false; /* SQL cannot be variadic "any" */ fexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */ fexpr->funccollid = result_collid; /* doesn't matter */ fexpr->inputcollid = input_collid; *** a/src/backend/parser/parse_func.c --- b/src/backend/parser/parse_func.c *************** *** 78,83 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 78,84 ---- List *argdefaults; Node *retval; bool retset; + bool merge_vararg; int nvargs; FuncDetailCode fdresult; *************** *** 214,221 **** 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, &merge_vararg, ! &nvargs, &declared_arg_types, &argdefaults); if (fdresult == FUNCDETAIL_COERCION) { /* *************** *** 384,389 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 385,391 ---- funcexpr->funcid = funcid; funcexpr->funcresulttype = rettype; funcexpr->funcretset = retset; + funcexpr->merge_vararg = merge_vararg; funcexpr->funcformat = COERCE_EXPLICIT_CALL; /* funccollid and inputcollid will be set by parse_collate.c */ funcexpr->args = fargs; *************** *** 1001,1006 **** func_select_candidate(int nargs, --- 1003,1017 ---- * they don't need that check made. Note also that when fargnames isn't NIL, * the fargs list must be passed if the caller wants actual argument position * information to be returned into the NamedArgExpr nodes. + * + * Special use case is a call variadic function with "any" variadic parameter and + * with variadic argument - argument market VARIADIC modifier. Variadic "any" + * function expect unpacked variadic arguments passed in fcinfo->args like usual + * parameter. Others variadic functions expect variadic argument as a array. So + * when we don't wont expand variadic parameter and we have variadic "any" + * function, we have to unpack a array to fcinfo->args array and we have to + * create short life FmgrInfo fake structure. When this use case is identified, + * then output in merge_vararg is true. */ FuncDetailCode func_get_detail(List *funcname, *************** *** 1013,1018 **** func_get_detail(List *funcname, --- 1024,1030 ---- Oid *funcid, /* return value */ Oid *rettype, /* return value */ bool *retset, /* return value */ + bool *merge_vararg, /* optional return value */ int *nvargs, /* return value */ Oid **true_typeids, /* return value */ List **argdefaults) /* optional return value */ *************** *** 1301,1306 **** func_get_detail(List *funcname, --- 1313,1322 ---- *argdefaults = defaults; } } + + if (merge_vararg != NULL) + *merge_vararg = !expand_variadic && pform->provariadic == ANYOID; + if (pform->proisagg) result = FUNCDETAIL_AGGREGATE; else if (pform->proiswindow) *** a/src/backend/utils/adt/ruleutils.c --- b/src/backend/utils/adt/ruleutils.c *************** *** 7437,7443 **** generate_function_name(Oid funcid, int nargs, List *argnames, NIL, argnames, nargs, argtypes, !OidIsValid(procform->provariadic), 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) && --- 7437,7443 ---- NIL, argnames, nargs, argtypes, !OidIsValid(procform->provariadic), true, &p_funcid, &p_rettype, ! &p_retset, NULL, &p_nvargs, &p_true_typeids, NULL); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE || p_result == FUNCDETAIL_WINDOWFUNC) && *** a/src/include/nodes/primnodes.h --- b/src/include/nodes/primnodes.h *************** *** 340,345 **** typedef struct FuncExpr --- 340,347 ---- Oid funcid; /* PG_PROC OID of the function */ Oid funcresulttype; /* PG_TYPE OID of result value */ bool funcretset; /* true if function returns set */ + bool merge_vararg; /* if true, then do unpack last array argument */ + /* to fmgr args */ CoercionForm funcformat; /* how to display this function call */ Oid funccollid; /* OID of collation of result */ Oid inputcollid; /* OID of collation that function should use */ *** a/src/include/parser/parse_func.h --- b/src/include/parser/parse_func.h *************** *** 53,59 **** 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, --- 53,60 ---- int nargs, Oid *argtypes, bool expand_variadic, bool expand_defaults, Oid *funcid, Oid *rettype, ! bool *retset, bool *merge_vararg, ! int *nvargs, Oid **true_typeids, List **argdefaults); extern int func_match_argtypes(int nargs, *** a/src/test/regress/expected/text.out --- b/src/test/regress/expected/text.out *************** *** 228,234 **** select format('%1$', 1); ERROR: unterminated conversion specifier select format('%1$1', 1); ERROR: unrecognized conversion specifier "1" ! --checkk mix of positional and ordered placeholders select format('Hello %s %1$s %s', 'World', 'Hello again'); format ------------------------------- --- 228,234 ---- ERROR: unterminated conversion specifier select format('%1$1', 1); ERROR: unrecognized conversion specifier "1" ! --check mix of positional and ordered placeholders select format('Hello %s %1$s %s', 'World', 'Hello again'); format ------------------------------- *************** *** 241,243 **** select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again'); --- 241,289 ---- Hello World Hello again, Hello again Hello again (1 row) + -- check pass variadic argument + select format('%s, %s', variadic array['Hello','World']); + format + -------------- + Hello, World + (1 row) + + -- multidimensional array is supported + select format('%s, %s', variadic array[['Nazdar','Svete'],['Hello','World']]); + format + ------------------------------- + {Nazdar,Svete}, {Hello,World} + (1 row) + + -- check others datatypes + select format('%s, %s', variadic array[1, 2]); + format + -------- + 1, 2 + (1 row) + + select format('%s, %s', variadic array[true, false]); + format + -------- + t, f + (1 row) + + select format('%s, %s', variadic array[true, false]::text[]); + format + ------------- + true, false + (1 row) + + -- check positional placeholders + select format('%2$s, %1$s', variadic array['first', 'second']); + format + --------------- + second, first + (1 row) + + select format('%2$s, %1$s', variadic array[1, 2]); + format + -------- + 2, 1 + (1 row) + *** a/src/test/regress/sql/text.sql --- b/src/test/regress/sql/text.sql *************** *** 73,78 **** select format('%1$s %13$s', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); select format('%1s', 1); select format('%1$', 1); select format('%1$1', 1); ! --checkk mix of positional and ordered placeholders select format('Hello %s %1$s %s', 'World', 'Hello again'); select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again'); --- 73,91 ---- select format('%1s', 1); select format('%1$', 1); select format('%1$1', 1); ! --check mix of positional and ordered placeholders select format('Hello %s %1$s %s', 'World', 'Hello again'); select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again'); + + -- check pass variadic argument + select format('%s, %s', variadic array['Hello','World']); + -- multidimensional array is supported + select format('%s, %s', variadic array[['Nazdar','Svete'],['Hello','World']]); + -- check others datatypes + select format('%s, %s', variadic array[1, 2]); + select format('%s, %s', variadic array[true, false]); + select format('%s, %s', variadic array[true, false]::text[]); + + -- check positional placeholders + select format('%2$s, %1$s', variadic array['first', 'second']); + select format('%2$s, %1$s', variadic array[1, 2]);