@@ -76,12 +76,12 @@ static bytea *bytea_substring(Datum str,
7676 bool length_not_specified );
7777static bytea * bytea_overlay (bytea * t1 , bytea * t2 , int sp , int sl );
7878static StringInfo makeStringAggState (FunctionCallInfo fcinfo );
79- void text_format_string_conversion (StringInfo buf , char conversion ,
80- Oid typid , Datum value , bool isNull );
81-
79+ static void text_format_string_conversion (StringInfo buf , char conversion ,
80+ FmgrInfo * typOutputInfo ,
81+ Datum value , bool isNull );
8282static Datum text_to_array_internal (PG_FUNCTION_ARGS );
8383static text * array_to_text_internal (FunctionCallInfo fcinfo , ArrayType * v ,
84- char * fldsep , char * null_string );
84+ const char * fldsep , const char * null_string );
8585
8686
8787/*****************************************************************************
@@ -3451,7 +3451,7 @@ array_to_text_null(PG_FUNCTION_ARGS)
34513451 */
34523452static text *
34533453array_to_text_internal (FunctionCallInfo fcinfo , ArrayType * v ,
3454- char * fldsep , char * null_string )
3454+ const char * fldsep , const char * null_string )
34553455{
34563456 text * result ;
34573457 int nitems ,
@@ -3791,18 +3791,61 @@ string_agg_finalfn(PG_FUNCTION_ARGS)
37913791/*
37923792 * Implementation of both concat() and concat_ws().
37933793 *
3794- * sepstr/seplen describe the separator. argidx is the first argument
3795- * to concatenate (counting from zero).
3794+ * sepstr is the separator string to place between values.
3795+ * argidx identifies the first argument to concatenate (counting from zero).
3796+ * Returns NULL if result should be NULL, else text value.
37963797 */
37973798static text *
3798- concat_internal (const char * sepstr , int seplen , int argidx ,
3799+ concat_internal (const char * sepstr , int argidx ,
37993800 FunctionCallInfo fcinfo )
38003801{
38013802 text * result ;
38023803 StringInfoData str ;
38033804 bool first_arg = true;
38043805 int i ;
38053806
3807+ /*
3808+ * concat(VARIADIC some-array) is essentially equivalent to
3809+ * array_to_text(), ie concat the array elements with the given separator.
3810+ * So we just pass the case off to that code.
3811+ */
3812+ if (get_fn_expr_variadic (fcinfo -> flinfo ))
3813+ {
3814+ Oid arr_typid ;
3815+ ArrayType * arr ;
3816+
3817+ /* Should have just the one argument */
3818+ Assert (argidx == PG_NARGS () - 1 );
3819+
3820+ /* concat(VARIADIC NULL) is defined as NULL */
3821+ if (PG_ARGISNULL (argidx ))
3822+ return NULL ;
3823+
3824+ /*
3825+ * Non-null argument had better be an array. The parser doesn't
3826+ * enforce this for VARIADIC ANY functions (maybe it should?), so that
3827+ * check uses ereport not just elog.
3828+ */
3829+ arr_typid = get_fn_expr_argtype (fcinfo -> flinfo , argidx );
3830+ if (!OidIsValid (arr_typid ))
3831+ elog (ERROR , "could not determine data type of concat() input" );
3832+
3833+ if (!OidIsValid (get_element_type (arr_typid )))
3834+ ereport (ERROR ,
3835+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
3836+ errmsg ("VARIADIC argument must be an array" )));
3837+
3838+ /* OK, safe to fetch the array value */
3839+ arr = PG_GETARG_ARRAYTYPE_P (argidx );
3840+
3841+ /*
3842+ * And serialize the array. We tell array_to_text to ignore null
3843+ * elements, which matches the behavior of the loop below.
3844+ */
3845+ return array_to_text_internal (fcinfo , arr , sepstr , NULL );
3846+ }
3847+
3848+ /* Normal case without explicit VARIADIC marker */
38063849 initStringInfo (& str );
38073850
38083851 for (i = argidx ; i < PG_NARGS (); i ++ )
@@ -3818,7 +3861,7 @@ concat_internal(const char *sepstr, int seplen, int argidx,
38183861 if (first_arg )
38193862 first_arg = false;
38203863 else
3821- appendBinaryStringInfo (& str , sepstr , seplen );
3864+ appendStringInfoString (& str , sepstr );
38223865
38233866 /* call the appropriate type output function, append the result */
38243867 valtype = get_fn_expr_argtype (fcinfo -> flinfo , i );
@@ -3842,7 +3885,12 @@ concat_internal(const char *sepstr, int seplen, int argidx,
38423885Datum
38433886text_concat (PG_FUNCTION_ARGS )
38443887{
3845- PG_RETURN_TEXT_P (concat_internal ("" , 0 , 0 , fcinfo ));
3888+ text * result ;
3889+
3890+ result = concat_internal ("" , 0 , fcinfo );
3891+ if (result == NULL )
3892+ PG_RETURN_NULL ();
3893+ PG_RETURN_TEXT_P (result );
38463894}
38473895
38483896/*
@@ -3852,16 +3900,18 @@ text_concat(PG_FUNCTION_ARGS)
38523900Datum
38533901text_concat_ws (PG_FUNCTION_ARGS )
38543902{
3855- text * sep ;
3903+ char * sep ;
3904+ text * result ;
38563905
38573906 /* return NULL when separator is NULL */
38583907 if (PG_ARGISNULL (0 ))
38593908 PG_RETURN_NULL ();
3909+ sep = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
38603910
3861- sep = PG_GETARG_TEXT_PP ( 0 );
3862-
3863- PG_RETURN_TEXT_P ( concat_internal ( VARDATA_ANY ( sep ), VARSIZE_ANY_EXHDR ( sep ),
3864- 1 , fcinfo ) );
3911+ result = concat_internal ( sep , 1 , fcinfo );
3912+ if ( result == NULL )
3913+ PG_RETURN_NULL ();
3914+ PG_RETURN_TEXT_P ( result );
38653915}
38663916
38673917/*
@@ -3959,11 +4009,73 @@ text_format(PG_FUNCTION_ARGS)
39594009 const char * end_ptr ;
39604010 text * result ;
39614011 int arg = 0 ;
4012+ bool funcvariadic ;
4013+ int nargs ;
4014+ Datum * elements = NULL ;
4015+ bool * nulls = NULL ;
4016+ Oid element_type = InvalidOid ;
4017+ Oid prev_type = InvalidOid ;
4018+ FmgrInfo typoutputfinfo ;
39624019
39634020 /* When format string is null, returns null */
39644021 if (PG_ARGISNULL (0 ))
39654022 PG_RETURN_NULL ();
39664023
4024+ /* If argument is marked VARIADIC, expand array into elements */
4025+ if (get_fn_expr_variadic (fcinfo -> flinfo ))
4026+ {
4027+ Oid arr_typid ;
4028+ ArrayType * arr ;
4029+ int16 elmlen ;
4030+ bool elmbyval ;
4031+ char elmalign ;
4032+ int nitems ;
4033+
4034+ /* Should have just the one argument */
4035+ Assert (PG_NARGS () == 2 );
4036+
4037+ /* If argument is NULL, we treat it as zero-length array */
4038+ if (PG_ARGISNULL (1 ))
4039+ nitems = 0 ;
4040+ else
4041+ {
4042+ /*
4043+ * Non-null argument had better be an array. The parser doesn't
4044+ * enforce this for VARIADIC ANY functions (maybe it should?), so
4045+ * that check uses ereport not just elog.
4046+ */
4047+ arr_typid = get_fn_expr_argtype (fcinfo -> flinfo , 1 );
4048+ if (!OidIsValid (arr_typid ))
4049+ elog (ERROR , "could not determine data type of format() input" );
4050+
4051+ if (!OidIsValid (get_element_type (arr_typid )))
4052+ ereport (ERROR ,
4053+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
4054+ errmsg ("VARIADIC argument must be an array" )));
4055+
4056+ /* OK, safe to fetch the array value */
4057+ arr = PG_GETARG_ARRAYTYPE_P (1 );
4058+
4059+ /* Get info about array element type */
4060+ element_type = ARR_ELEMTYPE (arr );
4061+ get_typlenbyvalalign (element_type ,
4062+ & elmlen , & elmbyval , & elmalign );
4063+
4064+ /* Extract all array elements */
4065+ deconstruct_array (arr , element_type , elmlen , elmbyval , elmalign ,
4066+ & elements , & nulls , & nitems );
4067+ }
4068+
4069+ nargs = nitems + 1 ;
4070+ funcvariadic = true;
4071+ }
4072+ else
4073+ {
4074+ /* Non-variadic case, we'll process the arguments individually */
4075+ nargs = PG_NARGS ();
4076+ funcvariadic = false;
4077+ }
4078+
39674079 /* Setup for main loop. */
39684080 fmt = PG_GETARG_TEXT_PP (0 );
39694081 start_ptr = VARDATA_ANY (fmt );
@@ -4062,26 +4174,54 @@ text_format(PG_FUNCTION_ARGS)
40624174 }
40634175
40644176 /* Not enough arguments? Deduct 1 to avoid counting format string. */
4065- if (arg > PG_NARGS () - 1 )
4177+ if (arg > nargs - 1 )
40664178 ereport (ERROR ,
40674179 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
40684180 errmsg ("too few arguments for format" )));
40694181
4182+ /* Get the value and type of the selected argument */
4183+ if (!funcvariadic )
4184+ {
4185+ value = PG_GETARG_DATUM (arg );
4186+ isNull = PG_ARGISNULL (arg );
4187+ typid = get_fn_expr_argtype (fcinfo -> flinfo , arg );
4188+ }
4189+ else
4190+ {
4191+ value = elements [arg - 1 ];
4192+ isNull = nulls [arg - 1 ];
4193+ typid = element_type ;
4194+ }
4195+ if (!OidIsValid (typid ))
4196+ elog (ERROR , "could not determine data type of format() input" );
4197+
4198+ /*
4199+ * Get the appropriate typOutput function, reusing previous one if
4200+ * same type as previous argument. That's particularly useful in the
4201+ * variadic-array case, but often saves work even for ordinary calls.
4202+ */
4203+ if (typid != prev_type )
4204+ {
4205+ Oid typoutputfunc ;
4206+ bool typIsVarlena ;
4207+
4208+ getTypeOutputInfo (typid , & typoutputfunc , & typIsVarlena );
4209+ fmgr_info (typoutputfunc , & typoutputfinfo );
4210+ prev_type = typid ;
4211+ }
4212+
40704213 /*
40714214 * At this point, we should see the main conversion specifier. Whether
40724215 * or not an argument position was present, it's known that at least
40734216 * one character remains in the string at this point.
40744217 */
4075- value = PG_GETARG_DATUM (arg );
4076- isNull = PG_ARGISNULL (arg );
4077- typid = get_fn_expr_argtype (fcinfo -> flinfo , arg );
4078-
40794218 switch (* cp )
40804219 {
40814220 case 's' :
40824221 case 'I' :
40834222 case 'L' :
4084- text_format_string_conversion (& str , * cp , typid , value , isNull );
4223+ text_format_string_conversion (& str , * cp , & typoutputfinfo ,
4224+ value , isNull );
40854225 break ;
40864226 default :
40874227 ereport (ERROR ,
@@ -4091,6 +4231,12 @@ text_format(PG_FUNCTION_ARGS)
40914231 }
40924232 }
40934233
4234+ /* Don't need deconstruct_array results anymore. */
4235+ if (elements != NULL )
4236+ pfree (elements );
4237+ if (nulls != NULL )
4238+ pfree (nulls );
4239+
40944240 /* Generate results. */
40954241 result = cstring_to_text_with_len (str .data , str .len );
40964242 pfree (str .data );
@@ -4099,12 +4245,11 @@ text_format(PG_FUNCTION_ARGS)
40994245}
41004246
41014247/* Format a %s, %I, or %L conversion. */
4102- void
4248+ static void
41034249text_format_string_conversion (StringInfo buf , char conversion ,
4104- Oid typid , Datum value , bool isNull )
4250+ FmgrInfo * typOutputInfo ,
4251+ Datum value , bool isNull )
41054252{
4106- Oid typOutput ;
4107- bool typIsVarlena ;
41084253 char * str ;
41094254
41104255 /* Handle NULL arguments before trying to stringify the value. */
@@ -4120,8 +4265,7 @@ text_format_string_conversion(StringInfo buf, char conversion,
41204265 }
41214266
41224267 /* Stringify. */
4123- getTypeOutputInfo (typid , & typOutput , & typIsVarlena );
4124- str = OidOutputFunctionCall (typOutput , value );
4268+ str = OutputFunctionCall (typOutputInfo , value );
41254269
41264270 /* Escape. */
41274271 if (conversion == 'I' )
0 commit comments