*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 1538,1543 ****
--- 1538,1561 ----
+
+
+ format_array
+
+ format_array(formatstr text
+ , params text[])
+
+ text
+
+ Format a string. This function is similar to function format.
+ It uses same formating rules. But this function is not variadic - parameters are
+ passed in array - second parameter of this function.
+
+ format_array('Hello %s, %1$s', array['World'])
+ Hello World, World
+
+
+
initcap
*** a/src/backend/catalog/namespace.c
--- b/src/backend/catalog/namespace.c
***************
*** 1048,1053 **** FuncnameGetCandidates(List *names, int nargs, List *argnames,
--- 1048,1061 ----
/* Ignore if it doesn't match requested argument count */
if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
continue;
+
+ /*
+ * Ignore variadic "ANY" function with disabled expand_variadic.
+ * There are no way, how to signalize target function about this
+ * situation.
+ */
+ if (!expand_variadic && procform->provariadic == ANYOID)
+ continue;
}
/*
*** a/src/backend/utils/adt/varlena.c
--- b/src/backend/utils/adt/varlena.c
***************
*** 50,55 **** typedef struct
--- 50,72 ----
int skiptable[256]; /* skip distance for given mismatched char */
} TextPositionState;
+ typedef struct
+ {
+ FunctionCallInfo fcinfo;
+ int nargs;
+ } text_format_context;
+
+ typedef struct
+ {
+ int nelems;
+ Datum *elem_values;
+ bool *elem_nulls;
+ Oid typOutput;
+ } text_format_array_context;
+
+ typedef void (*param_feeder_type) (StringInfo str, const char conversion,
+ int arg, void *context);
+
#define DatumGetUnknownP(X) ((unknown *) PG_DETOAST_DATUM(X))
#define DatumGetUnknownPCopy(X) ((unknown *) PG_DETOAST_DATUM_COPY(X))
#define PG_GETARG_UNKNOWN_P(n) DatumGetUnknownP(PG_GETARG_DATUM(n))
***************
*** 76,87 **** 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,
char *fldsep, char *null_string);
/*****************************************************************************
--- 93,107 ----
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,
! Datum value, bool isNull, Oid typOutput);
static Datum text_to_array_internal(PG_FUNCTION_ARGS);
static text *array_to_text_internal(FunctionCallInfo fcinfo, ArrayType *v,
char *fldsep, char *null_string);
+ static void text_format_add_converted_arg(StringInfo str,
+ const char conversion, int arg, void *context);
+ static text *text_format_internal(text *fmt, param_feeder_type param_feeder, void *context);
/*****************************************************************************
***************
*** 3946,3958 **** text_reverse(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P(result);
}
/*
! * Returns a formated string
*/
! Datum
! text_format(PG_FUNCTION_ARGS)
{
- text *fmt;
StringInfoData str;
const char *cp;
const char *start_ptr;
--- 3966,3978 ----
PG_RETURN_TEXT_P(result);
}
+
/*
! * generic function for format and format_array function
*/
! static text *
! text_format_internal(text *fmt, param_feeder_type param_feeder, void *context)
{
StringInfoData str;
const char *cp;
const char *start_ptr;
***************
*** 3960,3971 **** text_format(PG_FUNCTION_ARGS)
text *result;
int arg = 0;
- /* When format string is null, returns null */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL();
-
/* Setup for main loop. */
- fmt = PG_GETARG_TEXT_PP(0);
start_ptr = VARDATA_ANY(fmt);
end_ptr = start_ptr + VARSIZE_ANY_EXHDR(fmt);
initStringInfo(&str);
--- 3980,3986 ----
***************
*** 3973,3982 **** text_format(PG_FUNCTION_ARGS)
/* Scan format string, looking for conversion specifiers. */
for (cp = start_ptr; cp < end_ptr; cp++)
{
- Datum value;
- bool isNull;
- Oid typid;
-
/*
* If it's not the start of a conversion specifier, just copy it to
* the output buffer.
--- 3988,3993 ----
***************
*** 4061,4087 **** text_format(PG_FUNCTION_ARGS)
errmsg("conversion specifies argument 0, but arguments are numbered from 1")));
}
- /* 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")));
-
- /*
- * At this point, we should see the main conversion specifier. Whether
- * 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,
--- 4072,4083 ----
errmsg("conversion specifies argument 0, but arguments are numbered from 1")));
}
switch (*cp)
{
case 's':
case 'I':
case 'L':
! param_feeder(&str, *cp, arg, context);
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. */
--- 4091,4153 ----
result = cstring_to_text_with_len(str.data, str.len);
pfree(str.data);
! return result;
}
! /*
! * Assign formated parameter to output StringInfo
! */
! static void
! text_format_add_converted_arg(StringInfo str, const char conversion, int arg, void *context)
{
+ text_format_context *fctxt = (text_format_context *) context;
+ Datum value;
+ bool isNull;
+ Oid typid;
Oid typOutput;
bool typIsVarlena;
+
+ /* Not enough arguments? Deduct 1 to avoid counting format string. */
+ if (arg > fctxt->nargs - 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("too few arguments for format")));
+
+ value = fctxt->fcinfo->arg[arg];
+ isNull = fctxt->fcinfo->argnull[arg];
+ typid = get_fn_expr_argtype(fctxt->fcinfo->flinfo, arg);
+
+ /* Stringify. */
+ getTypeOutputInfo(typid, &typOutput, &typIsVarlena);
+
+ text_format_string_conversion(str, conversion, value, isNull, typOutput);
+ }
+
+ /*
+ * Returns a formated string
+ */
+ Datum
+ text_format(PG_FUNCTION_ARGS)
+ {
+ text_format_context fctxt;
+
+ /* When format string is null, returns null */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ fctxt.fcinfo = fcinfo;
+ fctxt.nargs = fcinfo->nargs;
+
+ PG_RETURN_TEXT_P(text_format_internal(PG_GETARG_TEXT_PP(0),
+ text_format_add_converted_arg,
+ &fctxt));
+ }
+
+ /* Format a %s, %I, or %L conversion. */
+ static void
+ text_format_string_conversion(StringInfo buf, char conversion,
+ Datum value, bool isNull, Oid typOutput)
+ {
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. */
--- 4163,4168 ----
***************
*** 4156,4158 **** text_format_nv(PG_FUNCTION_ARGS)
--- 4198,4272 ----
{
return text_format(fcinfo);
}
+
+ static void
+ text_format_array_add_converted_arg(StringInfo str, const char conversion, int arg, void *context)
+ {
+ text_format_array_context *fctxt = (text_format_array_context *) context;
+ Datum value;
+ bool isNull;
+
+ /* Not enough arguments? Deduct 1 to avoid counting format string. */
+ if (arg > fctxt->nelems)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("too few arguments for format")));
+
+ value = fctxt->elem_values[arg - 1];
+ isNull = fctxt->elem_nulls[arg - 1];
+
+ text_format_string_conversion(str, conversion, value, isNull, fctxt->typOutput);
+ }
+
+ /*
+ * Returns formated string - parameters are passed in array
+ */
+ Datum
+ text_format_array(PG_FUNCTION_ARGS)
+ {
+ text *result;
+ text_format_array_context fctxt;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ if (!PG_ARGISNULL(1))
+ {
+ ArrayType *v;
+ Oid elmtype;
+ int16 elmlen;
+ bool elmbyval;
+ char elmalign;
+ bool typbyval;
+
+ v = PG_GETARG_ARRAYTYPE_P(1);
+
+ elmtype = ARR_ELEMTYPE(v);
+ get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
+
+ deconstruct_array(v, elmtype,
+ elmlen, elmbyval, elmalign,
+ &fctxt.elem_values, &fctxt.elem_nulls,
+ &fctxt.nelems);
+
+ getTypeOutputInfo(elmtype, &fctxt.typOutput, &typbyval);
+ }
+ else
+ {
+ fctxt.nelems = 0;
+ fctxt.elem_values = NULL;
+ fctxt.elem_nulls = NULL;
+ fctxt.typOutput = InvalidOid;
+ }
+
+ result = text_format_internal(PG_GETARG_TEXT_PP(0),
+ text_format_array_add_converted_arg,
+ &fctxt);
+
+ if (fctxt.elem_values != NULL)
+ pfree(fctxt.elem_values);
+ if (fctxt.elem_nulls != NULL)
+ pfree(fctxt.elem_nulls);
+
+ PG_RETURN_TEXT_P(result);
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2317,2322 **** DATA(insert OID = 3539 ( format PGNSP PGUID 12 1 0 2276 0 f f f f f f s 2 0 25
--- 2317,2324 ----
DESCR("format text message");
DATA(insert OID = 3540 ( format PGNSP PGUID 12 1 0 0 0 f f f f f f s 1 0 25 "25" _null_ _null_ _null_ _null_ text_format_nv _null_ _null_ _null_ ));
DESCR("format text message");
+ DATA(insert OID = 3839 ( format_array PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 25 "25 2277" _null_ _null_ _null_ _null_ text_format_array _null_ _null_ _null_ ));
+ DESCR("format text message with array parameters");
DATA(insert OID = 1810 ( bit_length PGNSP PGUID 14 1 0 0 0 f f f f t f i 1 0 23 "17" _null_ _null_ _null_ _null_ "select pg_catalog.octet_length($1) * 8" _null_ _null_ _null_ ));
DESCR("length in bits");
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 790,795 **** extern Datum text_right(PG_FUNCTION_ARGS);
--- 790,796 ----
extern Datum text_reverse(PG_FUNCTION_ARGS);
extern Datum text_format(PG_FUNCTION_ARGS);
extern Datum text_format_nv(PG_FUNCTION_ARGS);
+ extern Datum text_format_array(PG_FUNCTION_ARGS);
/* version.c */
extern Datum pgsql_version(PG_FUNCTION_ARGS);
*** 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,262 ----
Hello World Hello again, Hello again Hello again
(1 row)
+ -- should fail - cannot use VARIADIC when you call variadic "any" function
+ select format('Hello %s %1$s %s', variadic array['World', 'Hello again']);
+ ERROR: function format(unknown, text[]) does not exist
+ LINE 1: select format('Hello %s %1$s %s', variadic array['World', 'H...
+ ^
+ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+ -- format array with format string
+ select format_array('Hello %s %1$s %s', array['World', 'Hello again']);
+ format_array
+ -------------------------------
+ Hello World World Hello again
+ (1 row)
+
+ select format_array('Hello %s %s, %2$s %2$s', array['World', 'Hello again']);
+ format_array
+ --------------------------------------------------
+ Hello World Hello again, Hello again Hello again
+ (1 row)
+
*** a/src/test/regress/sql/text.sql
--- b/src/test/regress/sql/text.sql
***************
*** 76,78 **** select format('%1$1', 1);
--- 76,85 ----
--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');
+
+ -- should fail - cannot use VARIADIC when you call variadic "any" function
+ select format('Hello %s %1$s %s', variadic array['World', 'Hello again']);
+
+ -- format array with format string
+ select format_array('Hello %s %1$s %s', array['World', 'Hello again']);
+ select format_array('Hello %s %s, %2$s %2$s', array['World', 'Hello again']);