Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit760f3c0

Browse files
committed
Fix concat() and format() to handle VARIADIC-labeled arguments correctly.
Previously, the VARIADIC labeling was effectively ignored, but now thesefunctions act as though the array elements had all been given as separatearguments.Pavel Stehule
1 parent56a6317 commit760f3c0

File tree

5 files changed

+310
-35
lines changed

5 files changed

+310
-35
lines changed

‎doc/src/sgml/func.sgml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,7 +1394,8 @@
13941394
</entry>
13951395
<entry><type>text</type></entry>
13961396
<entry>
1397-
Concatenate all arguments. NULL arguments are ignored.
1397+
Concatenate the text representations of all the arguments.
1398+
NULL arguments are ignored.
13981399
</entry>
13991400
<entry><literal>concat('abcde', 2, NULL, 22)</literal></entry>
14001401
<entry><literal>abcde222</literal></entry>
@@ -1411,8 +1412,8 @@
14111412
</entry>
14121413
<entry><type>text</type></entry>
14131414
<entry>
1414-
Concatenate all but firstarguments with separators. The first
1415-
parameter is used asa separator. NULL arguments are ignored.
1415+
Concatenate all butthefirstargument with separators. The first
1416+
argument is used asthe separator string. NULL arguments are ignored.
14161417
</entry>
14171418
<entry><literal>concat_ws(',', 'abcde', 2, NULL, 22)</literal></entry>
14181419
<entry><literal>abcde,2,22</literal></entry>
@@ -1522,8 +1523,9 @@
15221523
</entry>
15231524
<entry><type>text</type></entry>
15241525
<entry>
1525-
Format a string. This function is similar to the C function
1526-
<function>sprintf</>; but only the following conversion specifications
1526+
Format arguments according to a format string.
1527+
This function is similar to the C function
1528+
<function>sprintf</>, but only the following conversion specifications
15271529
are recognized: <literal>%s</literal> interpolates the corresponding
15281530
argument as a string; <literal>%I</literal> escapes its argument as
15291531
an SQL identifier; <literal>%L</literal> escapes its argument as an
@@ -2033,6 +2035,18 @@
20332035
</tgroup>
20342036
</table>
20352037

2038+
<para>
2039+
The <function>concat</function>, <function>concat_ws</function> and
2040+
<function>format</function> functions are variadic, so it is possible to
2041+
pass the values to be concatenated or formatted as an array marked with
2042+
the <literal>VARIADIC</literal> keyword (see <xref
2043+
linkend="xfunc-sql-variadic-functions">). The array's elements are
2044+
treated as if they were separate ordinary arguments to the function.
2045+
If the variadic array argument is NULL, <function>concat</function>
2046+
and <function>concat_ws</function> return NULL, but
2047+
<function>format</function> treats a NULL as a zero-element array.
2048+
</para>
2049+
20362050
<para>
20372051
See also the aggregate function <function>string_agg</function> in
20382052
<xref linkend="functions-aggregate">.

‎doc/src/sgml/xfunc.sgml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3153,6 +3153,10 @@ CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
31533153
<literal>fcinfo-&gt;flinfo</>. The parameter <literal>argnum</>
31543154
is zero based. <function>get_call_result_type</> can also be used
31553155
as an alternative to <function>get_fn_expr_rettype</>.
3156+
There is also <function>get_fn_expr_variadic</>, which can be used to
3157+
find out whether the call contained an explicit <literal>VARIADIC</>
3158+
keyword. This is primarily useful for <literal>VARIADIC "any"</>
3159+
functions, as described below.
31563160
</para>
31573161

31583162
<para>
@@ -3229,7 +3233,12 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
32293233
as happens with normal variadic functions; they will just be passed to
32303234
the function separately. The <function>PG_NARGS()</> macro and the
32313235
methods described above must be used to determine the number of actual
3232-
arguments and their types when using this feature.
3236+
arguments and their types when using this feature. Also, users of such
3237+
a function might wish to use the <literal>VARIADIC</> keyword in their
3238+
function call, with the expectation that the function would treat the
3239+
array elements as separate arguments. The function itself must implement
3240+
that behavior if wanted, after using <function>get_fn_expr_variadic</> to
3241+
detect that the actual argument was marked with <literal>VARIADIC</>.
32333242
</para>
32343243
</sect2>
32353244

‎src/backend/utils/adt/varlena.c

Lines changed: 171 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,12 @@ static bytea *bytea_substring(Datum str,
7676
boollength_not_specified);
7777
staticbytea*bytea_overlay(bytea*t1,bytea*t2,intsp,intsl);
7878
staticStringInfomakeStringAggState(FunctionCallInfofcinfo);
79-
voidtext_format_string_conversion(StringInfobuf,charconversion,
80-
Oidtypid,Datumvalue,boolisNull);
81-
79+
staticvoidtext_format_string_conversion(StringInfobuf,charconversion,
80+
FmgrInfo*typOutputInfo,
81+
Datumvalue,boolisNull);
8282
staticDatumtext_to_array_internal(PG_FUNCTION_ARGS);
8383
statictext*array_to_text_internal(FunctionCallInfofcinfo,ArrayType*v,
84-
char*fldsep,char*null_string);
84+
constchar*fldsep,constchar*null_string);
8585

8686

8787
/*****************************************************************************
@@ -3451,7 +3451,7 @@ array_to_text_null(PG_FUNCTION_ARGS)
34513451
*/
34523452
statictext*
34533453
array_to_text_internal(FunctionCallInfofcinfo,ArrayType*v,
3454-
char*fldsep,char*null_string)
3454+
constchar*fldsep,constchar*null_string)
34553455
{
34563456
text*result;
34573457
intnitems,
@@ -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
*/
37973798
statictext*
3798-
concat_internal(constchar*sepstr,intseplen,intargidx,
3799+
concat_internal(constchar*sepstr,intargidx,
37993800
FunctionCallInfofcinfo)
38003801
{
38013802
text*result;
38023803
StringInfoDatastr;
38033804
boolfirst_arg= true;
38043805
inti;
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+
Oidarr_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+
returnNULL;
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+
returnarray_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,
38423885
Datum
38433886
text_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)
38523900
Datum
38533901
text_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
constchar*end_ptr;
39604010
text*result;
39614011
intarg=0;
4012+
boolfuncvariadic;
4013+
intnargs;
4014+
Datum*elements=NULL;
4015+
bool*nulls=NULL;
4016+
Oidelement_type=InvalidOid;
4017+
Oidprev_type=InvalidOid;
4018+
FmgrInfotypoutputfinfo;
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+
Oidarr_typid;
4028+
ArrayType*arr;
4029+
int16elmlen;
4030+
boolelmbyval;
4031+
charelmalign;
4032+
intnitems;
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+
Oidtypoutputfunc;
4206+
booltypIsVarlena;
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+
staticvoid
41034249
text_format_string_conversion(StringInfobuf,charconversion,
4104-
Oidtypid,Datumvalue,boolisNull)
4250+
FmgrInfo*typOutputInfo,
4251+
Datumvalue,boolisNull)
41054252
{
4106-
OidtypOutput;
4107-
booltypIsVarlena;
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

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp