variadic_any_fix.patch
application/octet-stream
Filename: variadic_any_fix.patch
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/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 | 13 | 0 |
| src/backend/parser/parse_func.c | 1 | 0 |
| src/backend/utils/adt/varlena.c | 128 | 0 |
| src/backend/utils/fmgr/fmgr.c | 22 | 0 |
| src/include/fmgr.h | 1 | 0 |
| src/include/nodes/primnodes.h | 1 | 0 |
| src/test/regress/expected/text.out | 53 | 0 |
| src/test/regress/sql/text.sql | 17 | 0 |
*** 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(funcvariadic);
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(funcvariadic);
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->funcvariadic = 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(funcvariadic);
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(funcvariadic);
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
***************
*** 110,115 **** static Node *simplify_boolean_equality(Oid opno, List *args);
--- 110,116 ----
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List **args_p,
+ bool funcvariadic,
bool process_args, bool allow_non_const,
eval_const_expressions_context *context);
static List *expand_function_arguments(List *args, Oid result_type,
***************
*** 121,126 **** static void recheck_cast_function_args(List *args, Oid result_type,
--- 122,128 ----
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
+ bool funcvariadic,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
***************
*** 2314,2319 **** eval_const_expressions_mutator(Node *node,
--- 2316,2322 ----
expr->funccollid,
expr->inputcollid,
&args,
+ expr->funcvariadic,
true,
true,
context);
***************
*** 2330,2335 **** eval_const_expressions_mutator(Node *node,
--- 2333,2339 ----
newexpr->funcid = expr->funcid;
newexpr->funcresulttype = expr->funcresulttype;
newexpr->funcretset = expr->funcretset;
+ newexpr->funcvariadic = expr->funcvariadic;
newexpr->funcformat = expr->funcformat;
newexpr->funccollid = expr->funccollid;
newexpr->inputcollid = expr->inputcollid;
***************
*** 2359,2364 **** eval_const_expressions_mutator(Node *node,
--- 2363,2369 ----
expr->opcollid,
expr->inputcollid,
&args,
+ false,
true,
true,
context);
***************
*** 2464,2469 **** eval_const_expressions_mutator(Node *node,
--- 2469,2475 ----
&args,
false,
false,
+ false,
context);
if (simple) /* successfully simplified it */
{
***************
*** 2665,2670 **** eval_const_expressions_mutator(Node *node,
--- 2671,2677 ----
InvalidOid,
InvalidOid,
&args,
+ false,
true,
true,
context);
***************
*** 2697,2702 **** eval_const_expressions_mutator(Node *node,
--- 2704,2710 ----
InvalidOid,
&args,
false,
+ false,
true,
context);
if (simple) /* successfully simplified input fn */
***************
*** 3565,3570 **** simplify_boolean_equality(Oid opno, List *args)
--- 3573,3579 ----
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List **args_p,
+ bool funcvariadic,
bool process_args, bool allow_non_const,
eval_const_expressions_context *context)
{
***************
*** 3610,3615 **** simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
--- 3619,3625 ----
newexpr = evaluate_function(funcid, result_type, result_typmod,
result_collid, input_collid, args,
+ funcvariadic,
func_tuple, context);
if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
***************
*** 3625,3630 **** simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
--- 3635,3641 ----
fexpr.funcid = funcid;
fexpr.funcresulttype = result_type;
fexpr.funcretset = func_form->proretset;
+ fexpr.funcvariadic = funcvariadic;
fexpr.funcformat = COERCE_EXPLICIT_CALL;
fexpr.funccollid = result_collid;
fexpr.inputcollid = input_collid;
***************
*** 3878,3883 **** recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
--- 3889,3895 ----
static Expr *
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
+ bool funcvariadic,
HeapTuple func_tuple,
eval_const_expressions_context *context)
{
***************
*** 3959,3964 **** evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
--- 3971,3977 ----
newexpr->funcid = funcid;
newexpr->funcresulttype = result_type;
newexpr->funcretset = false;
+ newexpr->funcvariadic = funcvariadic;
newexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */
newexpr->funccollid = result_collid; /* doesn't matter */
newexpr->inputcollid = input_collid;
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
***************
*** 384,389 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 384,390 ----
funcexpr->funcid = funcid;
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = retset;
+ funcexpr->funcvariadic = func_variadic;
funcexpr->funcformat = COERCE_EXPLICIT_CALL;
/* funccollid and inputcollid will be set by parse_collate.c */
funcexpr->args = fargs;
*** a/src/backend/utils/adt/varlena.c
--- b/src/backend/utils/adt/varlena.c
***************
*** 76,83 **** static bytea *bytea_substring(Datum str,
bool length_not_specified);
static bytea *bytea_overlay(bytea *t1, bytea *t2, int sp, int sl);
static StringInfo makeStringAggState(FunctionCallInfo fcinfo);
! void text_format_string_conversion(StringInfo buf, char conversion,
! Oid typid, Datum value, bool isNull);
static Datum text_to_array_internal(PG_FUNCTION_ARGS);
static text *array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
--- 76,85 ----
bool length_not_specified);
static bytea *bytea_overlay(bytea *t1, bytea *t2, int sp, int sl);
static StringInfo makeStringAggState(FunctionCallInfo fcinfo);
! static void text_format_string_conversion(StringInfo buf, char conversion,
! Oid typOutput, Datum value, bool isNull);
! static void unpack_variadic(FunctionCallInfo fcinfo, int *nelems, Oid *vararg_typid,
! Datum **variadic_args, bool **variadic_nulls);
static Datum text_to_array_internal(PG_FUNCTION_ARGS);
static text *array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
***************
*** 3959,3964 **** text_format(PG_FUNCTION_ARGS)
--- 3961,3973 ----
const char *end_ptr;
text *result;
int arg = 0;
+ bool funcvariadic;
+ int nargs;
+ Oid vararg_typid = InvalidOid;
+ Datum *varargs = NULL;
+ bool *varargs_nulls = NULL;
+ Oid typOutput = InvalidOid;
+ Oid prev_typid = InvalidOid;
/* When format string is null, returns null */
if (PG_ARGISNULL(0))
***************
*** 3970,3975 **** text_format(PG_FUNCTION_ARGS)
--- 3979,4002 ----
end_ptr = start_ptr + VARSIZE_ANY_EXHDR(fmt);
initStringInfo(&str);
+ /* when last argument was marked as VARIADIC unpack variadic argument */
+ if (get_fn_expr_arg_variadic(fcinfo->flinfo))
+ {
+ int nelems;
+
+ unpack_variadic(fcinfo,
+ &nelems, &vararg_typid,
+ &varargs, &varargs_nulls);
+
+ nargs = PG_NARGS() - 1 + nelems;
+ funcvariadic = true;
+ }
+ else
+ {
+ nargs = PG_NARGS();
+ funcvariadic = false;
+ }
+
/* Scan format string, looking for conversion specifiers. */
for (cp = start_ptr; cp < end_ptr; cp++)
{
***************
*** 4062,4068 **** text_format(PG_FUNCTION_ARGS)
}
/* Not enough arguments? Deduct 1 to avoid counting format string. */
! if (arg > PG_NARGS() - 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("too few arguments for format")));
--- 4089,4095 ----
}
/* Not enough arguments? Deduct 1 to avoid counting format string. */
! if (arg > nargs - 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("too few arguments for format")));
***************
*** 4072,4087 **** text_format(PG_FUNCTION_ARGS)
* or not an argument position was present, it's known that at least
* one character remains in the string at this point.
*/
! value = PG_GETARG_DATUM(arg);
! isNull = PG_ARGISNULL(arg);
! typid = get_fn_expr_argtype(fcinfo->flinfo, arg);
switch (*cp)
{
case 's':
case 'I':
case 'L':
! text_format_string_conversion(&str, *cp, typid, value, isNull);
break;
default:
ereport(ERROR,
--- 4099,4132 ----
* or not an argument position was present, it's known that at least
* one character remains in the string at this point.
*/
! if (!funcvariadic)
! {
! value = PG_GETARG_DATUM(arg);
! isNull = PG_ARGISNULL(arg);
! typid = get_fn_expr_argtype(fcinfo->flinfo, arg);
! }
! else
! {
! value = varargs[arg - 1];
! isNull = varargs_nulls[arg - 1];
! typid = vararg_typid;
! }
!
! /* try to reuse typOutput function */
! if (typOutput == InvalidOid || prev_typid != typid)
! {
! bool typIsVarlena;
!
! getTypeOutputInfo(typid, &typOutput, &typIsVarlena);
! prev_typid = typid;
! }
switch (*cp)
{
case 's':
case 'I':
case 'L':
! text_format_string_conversion(&str, *cp, typOutput, value, isNull);
break;
default:
ereport(ERROR,
***************
*** 4095,4110 **** text_format(PG_FUNCTION_ARGS)
result = cstring_to_text_with_len(str.data, str.len);
pfree(str.data);
PG_RETURN_TEXT_P(result);
}
/* Format a %s, %I, or %L conversion. */
! void
text_format_string_conversion(StringInfo buf, char conversion,
! Oid typid, Datum value, bool isNull)
{
- Oid typOutput;
- bool typIsVarlena;
char *str;
/* Handle NULL arguments before trying to stringify the value. */
--- 4140,4158 ----
result = cstring_to_text_with_len(str.data, str.len);
pfree(str.data);
+ if (varargs != NULL)
+ pfree(varargs);
+ if (varargs_nulls != NULL)
+ pfree(varargs_nulls);
+
PG_RETURN_TEXT_P(result);
}
/* Format a %s, %I, or %L conversion. */
! static void
text_format_string_conversion(StringInfo buf, char conversion,
! Oid typOutput, Datum value, bool isNull)
{
char *str;
/* Handle NULL arguments before trying to stringify the value. */
***************
*** 4120,4126 **** text_format_string_conversion(StringInfo buf, char conversion,
}
/* Stringify. */
- getTypeOutputInfo(typid, &typOutput, &typIsVarlena);
str = OidOutputFunctionCall(typOutput, value);
/* Escape. */
--- 4168,4173 ----
***************
*** 4145,4150 **** text_format_string_conversion(StringInfo buf, char conversion,
--- 4192,4266 ----
}
/*
+ * unpack VARIADIC parameter - used by VARIADIC "any" functions, when
+ * parameter is labeled as VARIADIC
+ */
+ static void
+ unpack_variadic(FunctionCallInfo fcinfo, int *nelems, Oid *vararg_typid,
+ Datum **variadic_args, bool **variadic_nulls)
+ {
+ Oid arr_typid;
+ ArrayType *arr;
+ Oid elem_type;
+ int slice;
+ ArrayIterator iterator;
+ int nitems;
+ Datum *args;
+ bool *nulls;
+ Datum value;
+ bool isnull;
+ int n = 0;
+
+ /* sanity check, a variadic argument should be a non null array */
+ if (fcinfo->argnull[fcinfo->nargs - 1])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("variadic argument cannot be null")));
+
+ arr_typid = get_fn_expr_argtype(fcinfo->flinfo, fcinfo->nargs - 1);
+ if (!OidIsValid(arr_typid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determinate variadic argument data type")));
+
+ if (!OidIsValid(get_element_type(arr_typid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variadic argument should be an array")));
+
+ /* allow multidimensional arrays - use slicing when array is multidimensional */
+ arr = DatumGetArrayTypeP(fcinfo->arg[fcinfo->nargs - 1]);
+ slice = ARR_NDIM(arr) > 1 ? ARR_NDIM(arr) - 1 : 0;
+ elem_type = slice > 0 ? arr_typid : ARR_ELEMTYPE(arr);
+
+ iterator = array_create_iterator(arr, slice);
+ nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)) + 10;
+
+ /* preallocate memory for maximal number of items */
+ args = (Datum *) palloc(nitems * sizeof(Datum));
+ nulls = (bool *) palloc(nitems * sizeof(bool));
+
+ /* copy data from iterator */
+ while (array_iterate(iterator, &value, &isnull))
+ {
+ Assert(n < nitems);
+
+ args[n] = value;
+ nulls[n] = isnull;
+ n++;
+ }
+
+ /* Release iterator */
+ array_free_iterator(iterator);
+
+ /* push result */
+ *nelems = n;
+ *vararg_typid = elem_type;
+ *variadic_args = args;
+ *variadic_nulls = nulls;
+ }
+
+ /*
* text_format_nv - nonvariadic wrapper for text_format function.
*
* note: this wrapper is necessary to pass the sanity check in opr_sanity,
*** a/src/backend/utils/fmgr/fmgr.c
--- b/src/backend/utils/fmgr/fmgr.c
***************
*** 2307,2312 **** get_fn_expr_rettype(FmgrInfo *flinfo)
--- 2307,2334 ----
}
/*
+ * When last argument was labeled VARIADIC, then returns true,
+ * else returns false.
+ *
+ * Returns false if information is not available
+ */
+ bool
+ get_fn_expr_arg_variadic(FmgrInfo *flinfo)
+ {
+ /*
+ * can't return anything useful if we have no FmgrInfo or if its fn_expr
+ * node has not been initialized - when we have not available fn_expr, then
+ * label VARIADIC should not be used.
+ */
+ if (!flinfo || !flinfo->fn_expr)
+ return false;
+
+ Assert(IsA(flinfo->fn_expr, FuncExpr));
+
+ return ((FuncExpr *) flinfo->fn_expr)->funcvariadic;
+ }
+
+ /*
* Get the actual type OID of a specific function argument (counting from 0)
*
* Returns InvalidOid if information is not available
*** a/src/include/fmgr.h
--- b/src/include/fmgr.h
***************
*** 620,625 **** extern const Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcnam
--- 620,626 ----
extern void clear_external_function_hash(void *filehandle);
extern Oid fmgr_internal_function(const char *proname);
extern Oid get_fn_expr_rettype(FmgrInfo *flinfo);
+ extern bool get_fn_expr_arg_variadic(FmgrInfo *flinfo);
extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
extern Oid get_call_expr_argtype(fmNodePtr expr, int argnum);
extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 340,345 **** typedef struct FuncExpr
--- 340,346 ----
Oid funcid; /* PG_PROC OID of the function */
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
+ bool funcvariadic; /* last argument was labeled VARIADIC */
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/test/regress/expected/text.out
--- b/src/test/regress/expected/text.out
***************
*** 241,243 **** select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again');
--- 241,296 ----
Hello World Hello again, Hello again Hello again
(1 row)
+ -- check pass variadic labeled argument
+ select format('%s, %s', variadic array['Hello','World']);
+ format
+ --------------
+ Hello, World
+ (1 row)
+
+ -- multidimensional array is supported
+ select format('%s, %s', variadic array[['Yellow','submarine'],['Hello','World']]);
+ format
+ -----------------------------------
+ {Yellow,submarine}, {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)
+
+ -- check bypassing FUNC_MAX_ARGS limit via variadic labeled argument
+ select right(format(string_agg('%s',','), variadic array_agg(i)),31) from generate_series(1,500) g(i);
+ right
+ ---------------------------------
+ 493,494,495,496,497,498,499,500
+ (1 row)
+
*** a/src/test/regress/sql/text.sql
--- b/src/test/regress/sql/text.sql
***************
*** 76,78 **** select format('%1$1', 1);
--- 76,95 ----
--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');
+
+
+ -- check pass variadic labeled argument
+ select format('%s, %s', variadic array['Hello','World']);
+ -- multidimensional array is supported
+ select format('%s, %s', variadic array[['Yellow','submarine'],['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]);
+
+ -- check bypassing FUNC_MAX_ARGS limit via variadic labeled argument
+ select right(format(string_agg('%s',','), variadic array_agg(i)),31) from generate_series(1,500) g(i);