Skip to content

Commit c96de2c

Browse files
committed
Common function for percent placeholder replacement
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 <[email protected]> Discussion: https://www.postgresql.org/message-id/flat/5238bbed-0b01-83a6-d4b2-7eb0562a054e%40enterprisedb.com
1 parent 5f6401f commit c96de2c

File tree

10 files changed

+198
-246
lines changed

10 files changed

+198
-246
lines changed

contrib/basebackup_to_shell/basebackup_to_shell.c

+3-53
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "access/xact.h"
1414
#include "backup/basebackup_target.h"
15+
#include "common/percentrepl.h"
1516
#include "miscadmin.h"
1617
#include "storage/fd.h"
1718
#include "utils/acl.h"
@@ -208,59 +209,8 @@ static char *
208209
shell_construct_command(const char *base_command, const char *filename,
209210
const char *target_detail)
210211
{
211-
StringInfoData buf;
212-
const char *c;
213-
214-
initStringInfo(&buf);
215-
for (c = base_command; *c != '\0'; ++c)
216-
{
217-
/* Anything other than '%' is copied verbatim. */
218-
if (*c != '%')
219-
{
220-
appendStringInfoChar(&buf, *c);
221-
continue;
222-
}
223-
224-
/* Any time we see '%' we eat the following character as well. */
225-
++c;
226-
227-
/*
228-
* The following character determines what we insert here, or may
229-
* cause us to throw an error.
230-
*/
231-
if (*c == '%')
232-
{
233-
/* '%%' is replaced by a single '%' */
234-
appendStringInfoChar(&buf, '%');
235-
}
236-
else if (*c == 'f')
237-
{
238-
/* '%f' is replaced by the filename */
239-
appendStringInfoString(&buf, filename);
240-
}
241-
else if (*c == 'd')
242-
{
243-
/* '%d' is replaced by the target detail */
244-
appendStringInfoString(&buf, target_detail);
245-
}
246-
else if (*c == '\0')
247-
{
248-
/* Incomplete escape sequence, expected a character afterward */
249-
ereport(ERROR,
250-
errcode(ERRCODE_SYNTAX_ERROR),
251-
errmsg("shell command ends unexpectedly after escape character \"%%\""));
252-
}
253-
else
254-
{
255-
/* Unknown escape sequence */
256-
ereport(ERROR,
257-
errcode(ERRCODE_SYNTAX_ERROR),
258-
errmsg("shell command contains unexpected escape sequence \"%c\"",
259-
*c));
260-
}
261-
}
262-
263-
return buf.data;
212+
return replace_percent_placeholders(base_command, "basebackup_to_shell.command",
213+
"df", target_detail, filename);
264214
}
265215

266216
/*

src/backend/access/transam/xlogarchive.c

+5-40
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "access/xlog_internal.h"
2424
#include "access/xlogarchive.h"
2525
#include "common/archive.h"
26+
#include "common/percentrepl.h"
2627
#include "miscadmin.h"
2728
#include "pgstat.h"
2829
#include "postmaster/startup.h"
@@ -291,11 +292,8 @@ void
291292
ExecuteRecoveryCommand(const char *command, const char *commandName,
292293
bool failOnSignal, uint32 wait_event_info)
293294
{
294-
char xlogRecoveryCmd[MAXPGPATH];
295+
char *xlogRecoveryCmd;
295296
char lastRestartPointFname[MAXPGPATH];
296-
char *dp;
297-
char *endp;
298-
const char *sp;
299297
int rc;
300298
XLogSegNo restartSegNo;
301299
XLogRecPtr restartRedoPtr;
@@ -316,42 +314,7 @@ ExecuteRecoveryCommand(const char *command, const char *commandName,
316314
/*
317315
* construct the command to be executed
318316
*/
319-
dp = xlogRecoveryCmd;
320-
endp = xlogRecoveryCmd + MAXPGPATH - 1;
321-
*endp = '\0';
322-
323-
for (sp = command; *sp; sp++)
324-
{
325-
if (*sp == '%')
326-
{
327-
switch (sp[1])
328-
{
329-
case 'r':
330-
/* %r: filename of last restartpoint */
331-
sp++;
332-
strlcpy(dp, lastRestartPointFname, endp - dp);
333-
dp += strlen(dp);
334-
break;
335-
case '%':
336-
/* convert %% to a single % */
337-
sp++;
338-
if (dp < endp)
339-
*dp++ = *sp;
340-
break;
341-
default:
342-
/* otherwise treat the % as not special */
343-
if (dp < endp)
344-
*dp++ = *sp;
345-
break;
346-
}
347-
}
348-
else
349-
{
350-
if (dp < endp)
351-
*dp++ = *sp;
352-
}
353-
}
354-
*dp = '\0';
317+
xlogRecoveryCmd = replace_percent_placeholders(command, commandName, "r", lastRestartPointFname);
355318

356319
ereport(DEBUG3,
357320
(errmsg_internal("executing %s \"%s\"", commandName, command)));
@@ -364,6 +327,8 @@ ExecuteRecoveryCommand(const char *command, const char *commandName,
364327
rc = system(xlogRecoveryCmd);
365328
pgstat_report_wait_end();
366329

330+
pfree(xlogRecoveryCmd);
331+
367332
if (rc != 0)
368333
{
369334
/*

src/backend/libpq/be-secure-common.c

+8-30
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <sys/stat.h>
2323
#include <unistd.h>
2424

25+
#include "common/percentrepl.h"
2526
#include "common/string.h"
2627
#include "libpq/libpq.h"
2728
#include "storage/fd.h"
@@ -39,8 +40,7 @@ int
3940
run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
4041
{
4142
int loglevel = is_server_start ? ERROR : LOG;
42-
StringInfoData command;
43-
char *p;
43+
char *command;
4444
FILE *fh;
4545
int pclose_rc;
4646
size_t len = 0;
@@ -49,37 +49,15 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
4949
Assert(size > 0);
5050
buf[0] = '\0';
5151

52-
initStringInfo(&command);
52+
command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt);
5353

54-
for (p = ssl_passphrase_command; *p; p++)
55-
{
56-
if (p[0] == '%')
57-
{
58-
switch (p[1])
59-
{
60-
case 'p':
61-
appendStringInfoString(&command, prompt);
62-
p++;
63-
break;
64-
case '%':
65-
appendStringInfoChar(&command, '%');
66-
p++;
67-
break;
68-
default:
69-
appendStringInfoChar(&command, p[0]);
70-
}
71-
}
72-
else
73-
appendStringInfoChar(&command, p[0]);
74-
}
75-
76-
fh = OpenPipeStream(command.data, "r");
54+
fh = OpenPipeStream(command, "r");
7755
if (fh == NULL)
7856
{
7957
ereport(loglevel,
8058
(errcode_for_file_access(),
8159
errmsg("could not execute command \"%s\": %m",
82-
command.data)));
60+
command)));
8361
goto error;
8462
}
8563

@@ -91,7 +69,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
9169
ereport(loglevel,
9270
(errcode_for_file_access(),
9371
errmsg("could not read from command \"%s\": %m",
94-
command.data)));
72+
command)));
9573
goto error;
9674
}
9775
}
@@ -111,7 +89,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
11189
ereport(loglevel,
11290
(errcode_for_file_access(),
11391
errmsg("command \"%s\" failed",
114-
command.data),
92+
command),
11593
errdetail_internal("%s", wait_result_to_str(pclose_rc))));
11694
goto error;
11795
}
@@ -120,7 +98,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
12098
len = pg_strip_crlf(buf);
12199

122100
error:
123-
pfree(command.data);
101+
pfree(command);
124102
return len;
125103
}
126104

src/backend/postmaster/shell_archive.c

+13-48
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <sys/wait.h>
1919

2020
#include "access/xlog.h"
21+
#include "common/percentrepl.h"
2122
#include "pgstat.h"
2223
#include "postmaster/pgarch.h"
2324

@@ -44,58 +45,20 @@ shell_archive_configured(void)
4445
static bool
4546
shell_archive_file(const char *file, const char *path)
4647
{
47-
char xlogarchcmd[MAXPGPATH];
48-
char *dp;
49-
char *endp;
50-
const char *sp;
48+
char *xlogarchcmd;
49+
char *nativePath = NULL;
5150
int rc;
5251

53-
/*
54-
* construct the command to be executed
55-
*/
56-
dp = xlogarchcmd;
57-
endp = xlogarchcmd + MAXPGPATH - 1;
58-
*endp = '\0';
59-
60-
for (sp = XLogArchiveCommand; *sp; sp++)
52+
if (path)
6153
{
62-
if (*sp == '%')
63-
{
64-
switch (sp[1])
65-
{
66-
case 'p':
67-
/* %p: relative path of source file */
68-
sp++;
69-
strlcpy(dp, path, endp - dp);
70-
make_native_path(dp);
71-
dp += strlen(dp);
72-
break;
73-
case 'f':
74-
/* %f: filename of source file */
75-
sp++;
76-
strlcpy(dp, file, endp - dp);
77-
dp += strlen(dp);
78-
break;
79-
case '%':
80-
/* convert %% to a single % */
81-
sp++;
82-
if (dp < endp)
83-
*dp++ = *sp;
84-
break;
85-
default:
86-
/* otherwise treat the % as not special */
87-
if (dp < endp)
88-
*dp++ = *sp;
89-
break;
90-
}
91-
}
92-
else
93-
{
94-
if (dp < endp)
95-
*dp++ = *sp;
96-
}
54+
nativePath = pstrdup(path);
55+
make_native_path(nativePath);
9756
}
98-
*dp = '\0';
57+
58+
xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand, "archive_command", "fp", file, nativePath);
59+
60+
if (nativePath)
61+
pfree(nativePath);
9962

10063
ereport(DEBUG3,
10164
(errmsg_internal("executing archive command \"%s\"",
@@ -155,6 +118,8 @@ shell_archive_file(const char *file, const char *path)
155118
return false;
156119
}
157120

121+
pfree(xlogarchcmd);
122+
158123
elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
159124
return true;
160125
}

src/common/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ OBJS_COMMON = \
6565
kwlookup.o \
6666
link-canary.o \
6767
md5_common.o \
68+
percentrepl.o \
6869
pg_get_line.o \
6970
pg_lzcompress.o \
7071
pg_prng.o \

0 commit comments

Comments
 (0)