mirror of https://github.com/postgres/postgres
There are a number of places where a shell command is constructed with percent-placeholders (like %x). It's cumbersome to have to open-code this several times. This factors out this logic into a separate function. This also allows us to ensure consistency for and document some subtle behaviors, such as what to do with unrecognized placeholders. The unified handling is now that incorrect and unknown placeholders are an error, where previously in most cases they were skipped or ignored. This affects the following settings: - archive_cleanup_command - archive_command - recovery_end_command - restore_command - ssl_passphrase_command The following settings are part of this refactoring but already had stricter error handling and should be unchanged in their behavior: - basebackup_to_shell.command Reviewed-by: Nathan Bossart <nathandbossart@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/5238bbed-0b01-83a6-d4b2-7eb0562a054e%40enterprisedb.compull/113/head
parent
5f6401f81c
commit
c96de2ce17
@ -0,0 +1,137 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* percentrepl.c |
||||||
|
* Common routines to replace percent placeholders in strings |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2023, 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 escape sequence */ |
||||||
|
#ifdef FRONTEND |
||||||
|
pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr); |
||||||
|
pg_log_error_detail("String contains unexpected escape sequence \"%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 escape sequence \"%c\".", *sp)); |
||||||
|
#endif |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
appendStringInfoChar(&result, *sp); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result.data; |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* percentrepl.h |
||||||
|
* Common routines to replace percent placeholders in strings |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* src/include/common/percentrepl.h |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
#ifndef PERCENTREPL_H |
||||||
|
#define PERCENTREPL_H |
||||||
|
|
||||||
|
extern char *replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...); |
||||||
|
|
||||||
|
#endif /* PERCENTREPL_H */ |
Loading…
Reference in new issue