variadic_any_fix_20130121.patch

application/octet-stream

Filename: variadic_any_fix_20130121.patch
Type: application/octet-stream
Part: 0
Message: Re: proposal: fix corner use case of variadic fuctions usage

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 276 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 71 0
src/test/regress/sql/text.sql 23 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 text_format_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,
***************
*** 3789,3794 **** string_agg_finalfn(PG_FUNCTION_ARGS)
--- 3791,3920 ----
  }
  
  /*
+  * Holds state data for iteration over concat() and concat_ws() parameters
+  */
+ typedef struct ConcatIteratorData
+ {
+ 	bool	funcvariadic;			/* true when last parameter is labeled VARIADIC */
+ 	FunctionCallInfo   fcinfo;
+ 	int		   argidx;
+ 	int		   nargs;
+ 	ArrayIterator		iterator;
+ 	Oid			vararg_typid;
+ } ConcatIteratorData;
+ 
+ /*
+  * Initialise concat function argument iterator
+  *
+  * Functions concat() and concat_ws() are variadic "any" functions, so arguments
+  * can be passed directly in fcinfo->arg array or in array passed as first (concat())
+  * or second (concat_ws()) fcinfo->arg field. Iterator hides differences between
+  * both variants.
+  */
+ static void
+ initConcatIterator(ConcatIteratorData *ci, FunctionCallInfo fcinfo, int argidx)
+ {
+ 	MemSet(ci, 0, sizeof(ConcatIteratorData));
+ 
+ 	if (get_fn_expr_arg_variadic(fcinfo->flinfo))
+ 	{
+ 		Oid		 arr_typid;
+ 		ArrayType	*arr;
+ 		int		slice;
+ 
+ 		/* 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;
+ 
+ 		ci->vararg_typid = slice > 0 ? arr_typid : ARR_ELEMTYPE(arr);
+ 		ci->iterator = array_create_iterator(arr, slice);
+ 		ci->funcvariadic = true;
+ 	}
+ 	else
+ 	{
+ 		ci->argidx = argidx;
+ 		ci->fcinfo = fcinfo;
+ 		ci->nargs = PG_NARGS();
+ 		ci->funcvariadic = false;
+ 	}
+ }
+ 
+ /*
+  * set value of next concat*() function argument
+  *
+  * returns false when all parametes was processed
+  */
+ static bool
+ ConcatIteratorIterate(ConcatIteratorData *ci, Datum *value, bool *isnull, Oid *typid)
+ {
+ 	if (ci->funcvariadic)
+ 	{
+ 		Assert(ci->iterator != NULL);
+ 
+ 		/* get next array iterator field */
+ 		if (array_iterate(ci->iterator, value, isnull))
+ 		{
+ 			*typid = ci->vararg_typid;
+ 
+ 			return true;
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* get next FunctionCallInfo argument */
+ 		if (ci->argidx < ci->nargs)
+ 		{
+ 			if (!ci->fcinfo->argnull[ci->argidx])
+ 			{
+ 				*value = ci->fcinfo->arg[ci->argidx];
+ 				*typid = get_fn_expr_argtype(ci->fcinfo->flinfo, ci->argidx);
+ 				*isnull = false;
+ 			}
+ 			else
+ 				*isnull = true;
+ 
+ 			ci->argidx += 1;
+ 
+ 			return true;
+ 		}
+ 	}
+ 
+ 	return false;
+ }
+ 
+ /*
+  * release array iterator if was used
+  */
+ static void
+ ConcatIteratorFree(ConcatIteratorData *ci)
+ {
+ 	if (ci->funcvariadic)
+ 	{
+ 		Assert(ci->iterator != NULL);
+ 
+ 		/* Release iterator */
+ 		array_free_iterator(ci->iterator);
+ 	}
+ }
+ 
+ /*
   * Implementation of both concat() and concat_ws().
   *
   * sepstr/seplen describe the separator.  argidx is the first argument
***************
*** 3801,3818 **** concat_internal(const char *sepstr, int seplen, int argidx,
  	text	   *result;
  	StringInfoData str;
  	bool		first_arg = true;
! 	int			i;
  
  	initStringInfo(&str);
  
! 	for (i = argidx; i < PG_NARGS(); i++)
  	{
! 		if (!PG_ARGISNULL(i))
  		{
! 			Datum		value = PG_GETARG_DATUM(i);
! 			Oid			valtype;
! 			Oid			typOutput;
! 			bool		typIsVarlena;
  
  			/* add separator if appropriate */
  			if (first_arg)
--- 3927,3950 ----
  	text	   *result;
  	StringInfoData str;
  	bool		first_arg = true;
! 	Datum		value;
! 	bool		isnull;
! 	Oid			valtype;
! 	Oid			prev_typid = InvalidOid;
! 	Oid			typOutput = InvalidOid;
! 	ConcatIteratorData		ci;
  
  	initStringInfo(&str);
+ 	initConcatIterator(&ci, fcinfo, argidx);
  
! 	/* copy data from iterator */
! 	while (ConcatIteratorIterate(&ci, &value, &isnull, &valtype))
  	{
! 		if (!isnull)
  		{
! 			/* valtype must be defined */
! 			if (!OidIsValid(valtype))
! 				elog(ERROR, "could not determine data type of concat() input");
  
  			/* add separator if appropriate */
  			if (first_arg)
***************
*** 3820,3837 **** concat_internal(const char *sepstr, int seplen, int argidx,
  			else
  				appendBinaryStringInfo(&str, sepstr, seplen);
  
! 			/* call the appropriate type output function, append the result */
! 			valtype = get_fn_expr_argtype(fcinfo->flinfo, i);
! 			if (!OidIsValid(valtype))
! 				elog(ERROR, "could not determine data type of concat() input");
! 			getTypeOutputInfo(valtype, &typOutput, &typIsVarlena);
  			appendStringInfoString(&str,
! 								   OidOutputFunctionCall(typOutput, value));
  		}
  	}
  
  	result = cstring_to_text_with_len(str.data, str.len);
  	pfree(str.data);
  
  	return result;
  }
--- 3952,3974 ----
  			else
  				appendBinaryStringInfo(&str, sepstr, seplen);
  
! 			/* search typ output function when cannot to use previous */
! 			if (!OidIsValid(typOutput) || prev_typid != valtype)
! 			{
! 				bool typIsVarlena;
! 
! 				getTypeOutputInfo(valtype, &typOutput, &typIsVarlena);
! 				prev_typid = valtype;
! 			}
! 
  			appendStringInfoString(&str,
! 							   OidOutputFunctionCall(typOutput, value));
  		}
  	}
  
  	result = cstring_to_text_with_len(str.data, str.len);
  	pfree(str.data);
+ 	ConcatIteratorFree(&ci);
  
  	return result;
  }
***************
*** 3959,3964 **** text_format(PG_FUNCTION_ARGS)
--- 4096,4108 ----
  	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)
--- 4114,4137 ----
  	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;
+ 
+ 		text_format_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")));
--- 4224,4230 ----
  		}
  
  		/* 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,
--- 4234,4267 ----
  		 * 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. */
--- 4275,4293 ----
  	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. */
--- 4303,4308 ----
***************
*** 4145,4150 **** text_format_string_conversion(StringInfo buf, char conversion,
--- 4327,4401 ----
  }
  
  /*
+  * unpack VARIADIC parameter - used by VARIADIC "any" functions, when
+  * parameter is labeled as VARIADIC
+  */
+ static void
+ text_format_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
***************
*** 136,141 **** select quote_literal(e'\\');
--- 136,159 ----
   E'\\'
  (1 row)
  
+ -- check variadic labeled argument
+ select concat(variadic array[1,2,3]);
+  concat 
+ --------
+  123
+ (1 row)
+ 
+ select concat_ws(',', variadic array[1,2,3]);
+  concat_ws 
+ -----------
+  1,2,3
+ (1 row)
+ 
+ --should fail
+ select concat_ws(',', variadic 10);
+ ERROR:  variadic argument should be an array
+ select concat_ws(',', variadic NULL);
+ ERROR:  variadic argument cannot be null
  /*
   * format
   */
***************
*** 241,243 **** select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again');
--- 259,314 ----
   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
***************
*** 44,49 **** select i, left('ahoj', i), right('ahoj', i) from generate_series(-5, 5) t(i) ord
--- 44,55 ----
  select quote_literal('');
  select quote_literal('abc''');
  select quote_literal(e'\\');
+ -- check variadic labeled argument
+ select concat(variadic array[1,2,3]);
+ select concat_ws(',', variadic array[1,2,3]);
+ --should fail
+ select concat_ws(',', variadic 10);
+ select concat_ws(',', variadic NULL);
  
  /*
   * format
***************
*** 76,78 **** select format('%1$1', 1);
--- 82,101 ----
  --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);