variadic_argument_for_variadic_any_function.diff
application/octet-stream
Filename: variadic_argument_for_variadic_any_function.diff
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/backend/catalog/pg_aggregate.c | 2 | 0 |
| src/backend/executor/execQual.c | 106 | 0 |
| src/backend/nodes/copyfuncs.c | 1 | 0 |
| src/backend/nodes/equalfuncs.c | 1 | 0 |
| src/backend/nodes/makefuncs.c | 1 | 0 |
| src/backend/nodes/outfuncs.c | 1 | 0 |
| src/backend/nodes/readfuncs.c | 1 | 0 |
| src/backend/optimizer/util/clauses.c | 4 | 0 |
| src/backend/parser/parse_func.c | 18 | 0 |
| src/backend/utils/adt/ruleutils.c | 1 | 0 |
| src/include/nodes/primnodes.h | 2 | 0 |
| src/include/parser/parse_func.h | 2 | 0 |
| src/test/regress/expected/text.out | 47 | 0 |
| src/test/regress/sql/text.sql | 14 | 0 |
*** 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,1839 ----
}
/*
+ * 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 */
+ FunctionCallInfoData 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);
+
+ /* 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 = 1;
+ params_exprs = NIL;
+ foreach(lc, fexpr->args)
+ {
+ /* don't copy last argument */
+ if (n++ >= fcinfo->nargs)
+ break;
+ params_exprs = lappend(params_exprs, lc->data.ptr_value);
+ }
+
+ while (array_iterate(iterator, &value, &isnull))
+ {
+ /* replace variadic argument */
+ scinfo.arg[n - 2] = value;
+ scinfo.argnull[n - 2] = 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]);