variadic_any_fix_20130124.patch
application/octet-stream
Filename: variadic_any_fix_20130124.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 | + | − |
|---|---|---|
| doc/src/sgml/func.sgml | 10 | 0 |
| src/backend/utils/adt/varlena.c | 178 | 0 |
| src/test/regress/expected/text.out | 87 | 0 |
| src/test/regress/sql/text.sql | 25 | 0 |
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 2034,2039 ****
--- 2034,2049 ----
</table>
<para>
+ Functions <function>concat</function>, <function>concat_ws</function> and
+ <function>format</function> are variadic - variadic arguments should be passed
+ as usual or as array that is labeled as <literal>VARIADIC</literal>.
+ Functions <function>concat</function> and <function>concat_ws</function>
+ returns NULL, when variadic array argument is NULL and empty string
+ when variadic array argument is empty array. Result of <function>format</function>
+ primary depends on content of fmt string.
+ </para>
+
+ <para>
See also the aggregate function <function>string_agg</function> in
<xref linkend="functions-aggregate">.
</para>
*** 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,83 ----
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 Datum text_to_array_internal(PG_FUNCTION_ARGS);
static text *array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
***************
*** 3792,3839 **** string_agg_finalfn(PG_FUNCTION_ARGS)
* Implementation of both concat() and concat_ws().
*
* sepstr/seplen describe the separator. argidx is the first argument
! * to concatenate (counting from zero).
*/
static text *
! concat_internal(const char *sepstr, int seplen, int argidx,
FunctionCallInfo fcinfo)
{
! 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)
! first_arg = false;
! 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;
}
/*
--- 3792,3877 ----
* Implementation of both concat() and concat_ws().
*
* sepstr/seplen describe the separator. argidx is the first argument
! * to concatenate (counting from zero). When argument with variadic
! * array is NULL, then returns NULL, else string or empty string.
*/
static text *
! concat_internal(char *sepstr, int argidx,
FunctionCallInfo fcinfo)
{
! /*
! * When this routine is called from variadic function with
! * labeled VARIADIC parameter, then sematic is very close to
! * array_to_string function - and we can use code
! * array_to_text_internal, that is well optimized.
! */
! if (get_fn_expr_variadic(fcinfo->flinfo))
! {
! Oid arr_typid;
! ArrayType *arr;
! /* do nothing, when argument is NULL */
! if (fcinfo->argnull[fcinfo->nargs - 1])
! return NULL;
!
! /* sanity check - any array type is requested */
! 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")));
! /* now is safe get the array */
! arr = DatumGetArrayTypeP(fcinfo->arg[fcinfo->nargs - 1]);
!
! /* serialize an array */
! return array_to_text_internal(fcinfo, arr, sepstr, NULL);
! }
! else
{
! text *result;
! StringInfoData str;
! bool first_arg = true;
! int i;
!
! /* process variadic "any" parameters stored as FunctionCallInfo arguments */
! 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)
! first_arg = false;
! else
! appendStringInfoString(&str, sepstr);
!
! /* 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;
! }
}
/*
***************
*** 3842,3848 **** concat_internal(const char *sepstr, int seplen, int argidx,
Datum
text_concat(PG_FUNCTION_ARGS)
{
! PG_RETURN_TEXT_P(concat_internal("", 0, 0, fcinfo));
}
/*
--- 3880,3892 ----
Datum
text_concat(PG_FUNCTION_ARGS)
{
! text *result;
!
! result = concat_internal("", 0, fcinfo);
! if (result == NULL)
! PG_RETURN_NULL();
!
! PG_RETURN_TEXT_P(result);
}
/*
***************
*** 3852,3867 **** text_concat(PG_FUNCTION_ARGS)
Datum
text_concat_ws(PG_FUNCTION_ARGS)
{
! text *sep;
/* return NULL when separator is NULL */
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
! sep = PG_GETARG_TEXT_PP(0);
! PG_RETURN_TEXT_P(concat_internal(VARDATA_ANY(sep), VARSIZE_ANY_EXHDR(sep),
! 1, fcinfo));
}
/*
--- 3896,3915 ----
Datum
text_concat_ws(PG_FUNCTION_ARGS)
{
! char *sep;
! text *result;
/* return NULL when separator is NULL */
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
! sep = text_to_cstring(PG_GETARG_TEXT_PP(0));
! result = concat_internal(sep, 1, fcinfo);
! if (result == NULL)
! PG_RETURN_NULL();
!
! PG_RETURN_TEXT_P(result);
}
/*
***************
*** 3959,3969 **** text_format(PG_FUNCTION_ARGS)
--- 4007,4079 ----
const char *end_ptr;
text *result;
int arg = 0;
+ bool funcvariadic;
+ int nargs;
+ Datum *elements = NULL;
+ bool *nulls = NULL;
+ Oid element_type = InvalidOid;
+ Oid prev_type = InvalidOid;
+ Oid typoutputfunc = InvalidOid;
/* When format string is null, returns null */
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
+ /* when last argument was marked as VARIADIC sanitize variadic argument */
+ if (get_fn_expr_variadic(fcinfo->flinfo))
+ {
+ Oid array_type;
+ ArrayType *arr;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+ Oid typioparam;
+ int nitems = 0;
+
+ /*
+ * Variadic argument should be NULL in this case, becase there should not
+ * be any reference from fmt string.
+ */
+ if (!PG_ARGISNULL(1))
+ {
+ /* sanity check - any array type is requested */
+ array_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!OidIsValid(array_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determinate variadic argument data type")));
+
+ if (!OidIsValid(get_element_type(array_type)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("variadic argument should be an array")));
+
+ /* now we know so variadic "any" argument is really array */
+ arr = PG_GETARG_ARRAYTYPE_P(1);
+ element_type = ARR_ELEMTYPE(arr);
+
+ get_type_io_data(element_type, IOFunc_output,
+ &typlen, &typbyval, &typalign,
+ &typdelim, &typioparam, &typoutputfunc);
+
+ /* we know typoutput function, so we can set prev_typid */
+ prev_type = element_type;
+
+ deconstruct_array(arr, element_type, typlen, typbyval,
+ typalign, &elements, &nulls,
+ &nitems);
+ }
+
+ nargs = nitems + 1;
+ funcvariadic = true;
+ }
+ else
+ {
+ nargs = PG_NARGS();
+ funcvariadic = false;
+ }
+
/* Setup for main loop. */
fmt = PG_GETARG_TEXT_PP(0);
start_ptr = VARDATA_ANY(fmt);
***************
*** 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")));
--- 4172,4178 ----
}
/* 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,
--- 4182,4215 ----
* 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 = elements[arg - 1];
! isNull = nulls[arg - 1];
! typid = element_type;
! }
!
! /* try to reuse typOutput function */
! if (typoutputfunc == InvalidOid || prev_type != typid)
! {
! bool typIsVarlena;
!
! getTypeOutputInfo(typid, &typoutputfunc, &typIsVarlena);
! prev_type = typid;
! }
switch (*cp)
{
case 's':
case 'I':
case 'L':
! text_format_string_conversion(&str, *cp, typoutputfunc, 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. */
--- 4223,4241 ----
result = cstring_to_text_with_len(str.data, str.len);
pfree(str.data);
+ if (elements != NULL)
+ pfree(elements);
+ if (nulls != NULL)
+ pfree(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. */
--- 4251,4256 ----
*** a/src/test/regress/expected/text.out
--- b/src/test/regress/expected/text.out
***************
*** 136,141 **** select quote_literal(e'\\');
--- 136,175 ----
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)
+
+ select concat_ws(',', variadic NULL);
+ concat_ws
+ -----------
+
+ (1 row)
+
+ select concat(variadic NULL) is NULL;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select concat(variadic '{}'::int[]) = '';
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ --should fail
+ select concat_ws(',', variadic 10);
+ ERROR: variadic argument should be an array
/*
* format
*/
***************
*** 241,243 **** select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again');
--- 275,330 ----
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)
+
+ -- 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)
+
+ -- variadic argument can be NULL, but should not be referenced
+ select format('Hello', variadic NULL);
+ format
+ --------
+ Hello
+ (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,57 ----
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]);
+ select concat_ws(',', variadic NULL);
+ select concat(variadic NULL) is NULL;
+ select concat(variadic '{}'::int[]) = '';
+ --should fail
+ select concat_ws(',', variadic 10);
/*
* format
***************
*** 76,78 **** select format('%1$1', 1);
--- 84,103 ----
--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']);
+ -- 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]);
+
+ -- variadic argument can be NULL, but should not be referenced
+ select format('Hello', variadic NULL);
+
+ -- 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);