*** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 1530,1536 **** SQL literal; %% outputs a literal %. A conversion can reference an explicit parameter position by preceding the conversion specifier with n$, where ! n is the argument position. See also . format('Hello %s, %1$s', 'World') --- 1530,1537 ---- SQL literal; %% outputs a literal %. A conversion can reference an explicit parameter position by preceding the conversion specifier with n$, where ! n is the argument position. A warnig is raised ! when positional and ordered placeholders are used together. See also . format('Hello %s, %1$s', 'World') *** a/src/backend/utils/adt/varlena.c --- b/src/backend/utils/adt/varlena.c *************** *** 3958,3964 **** text_format(PG_FUNCTION_ARGS) const char *start_ptr; const char *end_ptr; text *result; ! int arg = 0; /* When format string is null, returns null */ if (PG_ARGISNULL(0)) --- 3958,3968 ---- const char *start_ptr; const char *end_ptr; text *result; ! int arg; ! int position = 0; ! bool positional = false; ! bool ordered = false; ! bool warning_emmited = false; /* When format string is null, returns null */ if (PG_ARGISNULL(0)) *************** *** 4006,4013 **** text_format(PG_FUNCTION_ARGS) */ if (*cp < '0' || *cp > '9') { ! ++arg; ! if (arg <= 0) /* overflow? */ { /* * Should not happen, as you can't pass billions of arguments --- 4010,4025 ---- */ if (*cp < '0' || *cp > '9') { ! /* don't allow mix styles - reflects glibc behave */ ! if (positional && !warning_emmited) ! { ! elog(WARNING, "ordered and positional placeholders are used together"); ! warning_emmited = true; ! } ! ordered = true; ! ! ++position; ! if (position <= 0) /* overflow? */ { /* * Should not happen, as you can't pass billions of arguments *************** *** 4017,4022 **** text_format(PG_FUNCTION_ARGS) --- 4029,4035 ---- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("argument number is out of range"))); } + arg = position; } else { *************** *** 4054,4059 **** text_format(PG_FUNCTION_ARGS) --- 4067,4079 ---- (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unterminated conversion specifier"))); + if (ordered && !warning_emmited) + { + elog(WARNING, "ordered and positional placeholders are used together"); + warning_emmited = true; + } + positional = true; + /* There's no argument 0. */ if (arg == 0) ereport(ERROR, *** a/src/test/regress/expected/text.out --- b/src/test/regress/expected/text.out *************** *** 229,243 **** ERROR: unterminated conversion specifier --- 229,253 ---- select format('%1$1', 1); ERROR: unrecognized conversion specifier "1" --checkk mix of positional and ordered placeholders + --should raise warnings select format('Hello %s %1$s %s', 'World', 'Hello again'); + WARNING: ordered and positional placeholders are used together format ------------------------------- Hello World World Hello again (1 row) select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again'); + WARNING: ordered and positional placeholders are used together format -------------------------------------------------- Hello World Hello again, Hello again Hello again (1 row) + select format('Hello %s %2$s %s', 'World', 'Hello again'); + WARNING: ordered and positional placeholders are used together + format + ------------------------------------- + Hello World Hello again Hello again + (1 row) + *** a/src/test/regress/sql/text.sql --- b/src/test/regress/sql/text.sql *************** *** 74,78 **** select format('%1s', 1); --- 74,80 ---- select format('%1$', 1); select format('%1$1', 1); --checkk mix of positional and ordered placeholders + --should raise warnings select format('Hello %s %1$s %s', 'World', 'Hello again'); select format('Hello %s %s, %2$s %2$s', 'World', 'Hello again'); + select format('Hello %s %2$s %s', 'World', 'Hello again');