mirror of
https://github.com/postgres/postgres.git
synced 2025-05-05 09:19:17 +03:00
Reported-by: Michael Paquier Discussion: https://postgr.es/m/ZZKTDPxBBMt3C0J9@paquier.xyz Backpatch-through: 12
138 lines
3.8 KiB
C
138 lines
3.8 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* percentrepl.c
|
|
* Common routines to replace percent placeholders in strings
|
|
*
|
|
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/common/percentrepl.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#ifndef FRONTEND
|
|
#include "postgres.h"
|
|
#else
|
|
#include "postgres_fe.h"
|
|
#include "common/logging.h"
|
|
#endif
|
|
|
|
#include "common/percentrepl.h"
|
|
#include "lib/stringinfo.h"
|
|
|
|
/*
|
|
* replace_percent_placeholders
|
|
*
|
|
* Replace percent-letter placeholders in input string with the supplied
|
|
* values. For example, to replace %f with foo and %b with bar, call
|
|
*
|
|
* replace_percent_placeholders(instr, "param_name", "bf", bar, foo);
|
|
*
|
|
* The return value is palloc'd.
|
|
*
|
|
* "%%" is replaced by a single "%".
|
|
*
|
|
* This throws an error for an unsupported placeholder or a "%" at the end of
|
|
* the input string.
|
|
*
|
|
* A value may be NULL. If the corresponding placeholder is found in the
|
|
* input string, it will be treated as if an unsupported placeholder was used.
|
|
* This allows callers to share a "letters" specification but vary the
|
|
* actually supported placeholders at run time.
|
|
*
|
|
* This functions is meant for cases where all the values are readily
|
|
* available or cheap to compute and most invocations will use most values
|
|
* (for example for archive_command). Also, it requires that all values are
|
|
* strings. It won't be a good match for things like log prefixes or prompts
|
|
* that use a mix of data types and any invocation will only use a few of the
|
|
* possible values.
|
|
*
|
|
* param_name is the name of the underlying GUC parameter, for error
|
|
* reporting. At the moment, this function is only used for GUC parameters.
|
|
* If other kinds of uses were added, the error reporting would need to be
|
|
* revised.
|
|
*/
|
|
char *
|
|
replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...)
|
|
{
|
|
StringInfoData result;
|
|
|
|
initStringInfo(&result);
|
|
|
|
for (const char *sp = instr; *sp; sp++)
|
|
{
|
|
if (*sp == '%')
|
|
{
|
|
if (sp[1] == '%')
|
|
{
|
|
/* Convert %% to a single % */
|
|
sp++;
|
|
appendStringInfoChar(&result, *sp);
|
|
}
|
|
else if (sp[1] == '\0')
|
|
{
|
|
/* Incomplete escape sequence, expected a character afterward */
|
|
#ifdef FRONTEND
|
|
pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
|
|
pg_log_error_detail("String ends unexpectedly after escape character \"%%\".");
|
|
exit(1);
|
|
#else
|
|
ereport(ERROR,
|
|
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
|
|
errdetail("String ends unexpectedly after escape character \"%%\"."));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* Look up placeholder character */
|
|
bool found = false;
|
|
va_list ap;
|
|
|
|
sp++;
|
|
|
|
va_start(ap, letters);
|
|
for (const char *lp = letters; *lp; lp++)
|
|
{
|
|
char *val = va_arg(ap, char *);
|
|
|
|
if (*sp == *lp)
|
|
{
|
|
if (val)
|
|
{
|
|
appendStringInfoString(&result, val);
|
|
found = true;
|
|
}
|
|
/* If val is NULL, we will report an error. */
|
|
break;
|
|
}
|
|
}
|
|
va_end(ap);
|
|
if (!found)
|
|
{
|
|
/* Unknown placeholder */
|
|
#ifdef FRONTEND
|
|
pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
|
|
pg_log_error_detail("String contains unexpected placeholder \"%%%c\".", *sp);
|
|
exit(1);
|
|
#else
|
|
ereport(ERROR,
|
|
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
|
|
errdetail("String contains unexpected placeholder \"%%%c\".", *sp));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
appendStringInfoChar(&result, *sp);
|
|
}
|
|
}
|
|
|
|
return result.data;
|
|
}
|