aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/transam/xlogarchive.c45
-rw-r--r--src/backend/libpq/be-secure-common.c38
-rw-r--r--src/backend/postmaster/shell_archive.c61
-rw-r--r--src/common/Makefile1
-rw-r--r--src/common/archive.c85
-rw-r--r--src/common/meson.build1
-rw-r--r--src/common/percentrepl.c137
-rw-r--r--src/fe_utils/archive.c2
-rw-r--r--src/include/common/percentrepl.h18
9 files changed, 195 insertions, 193 deletions
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index 76abc74c678..f911e8c3a6f 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -23,6 +23,7 @@
#include "access/xlog_internal.h"
#include "access/xlogarchive.h"
#include "common/archive.h"
+#include "common/percentrepl.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/startup.h"
@@ -291,11 +292,8 @@ void
ExecuteRecoveryCommand(const char *command, const char *commandName,
bool failOnSignal, uint32 wait_event_info)
{
- char xlogRecoveryCmd[MAXPGPATH];
+ char *xlogRecoveryCmd;
char lastRestartPointFname[MAXPGPATH];
- char *dp;
- char *endp;
- const char *sp;
int rc;
XLogSegNo restartSegNo;
XLogRecPtr restartRedoPtr;
@@ -316,42 +314,7 @@ ExecuteRecoveryCommand(const char *command, const char *commandName,
/*
* construct the command to be executed
*/
- dp = xlogRecoveryCmd;
- endp = xlogRecoveryCmd + MAXPGPATH - 1;
- *endp = '\0';
-
- for (sp = command; *sp; sp++)
- {
- if (*sp == '%')
- {
- switch (sp[1])
- {
- case 'r':
- /* %r: filename of last restartpoint */
- sp++;
- strlcpy(dp, lastRestartPointFname, endp - dp);
- dp += strlen(dp);
- break;
- case '%':
- /* convert %% to a single % */
- sp++;
- if (dp < endp)
- *dp++ = *sp;
- break;
- default:
- /* otherwise treat the % as not special */
- if (dp < endp)
- *dp++ = *sp;
- break;
- }
- }
- else
- {
- if (dp < endp)
- *dp++ = *sp;
- }
- }
- *dp = '\0';
+ xlogRecoveryCmd = replace_percent_placeholders(command, commandName, "r", lastRestartPointFname);
ereport(DEBUG3,
(errmsg_internal("executing %s \"%s\"", commandName, command)));
@@ -364,6 +327,8 @@ ExecuteRecoveryCommand(const char *command, const char *commandName,
rc = system(xlogRecoveryCmd);
pgstat_report_wait_end();
+ pfree(xlogRecoveryCmd);
+
if (rc != 0)
{
/*
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
index f6078c91c34..ab5e2dfa2bf 100644
--- a/src/backend/libpq/be-secure-common.c
+++ b/src/backend/libpq/be-secure-common.c
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "common/percentrepl.h"
#include "common/string.h"
#include "libpq/libpq.h"
#include "storage/fd.h"
@@ -39,8 +40,7 @@ int
run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
{
int loglevel = is_server_start ? ERROR : LOG;
- StringInfoData command;
- char *p;
+ char *command;
FILE *fh;
int pclose_rc;
size_t len = 0;
@@ -49,37 +49,15 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
Assert(size > 0);
buf[0] = '\0';
- initStringInfo(&command);
+ command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt);
- for (p = ssl_passphrase_command; *p; p++)
- {
- if (p[0] == '%')
- {
- switch (p[1])
- {
- case 'p':
- appendStringInfoString(&command, prompt);
- p++;
- break;
- case '%':
- appendStringInfoChar(&command, '%');
- p++;
- break;
- default:
- appendStringInfoChar(&command, p[0]);
- }
- }
- else
- appendStringInfoChar(&command, p[0]);
- }
-
- fh = OpenPipeStream(command.data, "r");
+ fh = OpenPipeStream(command, "r");
if (fh == NULL)
{
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not execute command \"%s\": %m",
- command.data)));
+ command)));
goto error;
}
@@ -91,7 +69,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not read from command \"%s\": %m",
- command.data)));
+ command)));
goto error;
}
}
@@ -111,7 +89,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
ereport(loglevel,
(errcode_for_file_access(),
errmsg("command \"%s\" failed",
- command.data),
+ command),
errdetail_internal("%s", wait_result_to_str(pclose_rc))));
goto error;
}
@@ -120,7 +98,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
len = pg_strip_crlf(buf);
error:
- pfree(command.data);
+ pfree(command);
return len;
}
diff --git a/src/backend/postmaster/shell_archive.c b/src/backend/postmaster/shell_archive.c
index 6f98414a408..806b81c3f22 100644
--- a/src/backend/postmaster/shell_archive.c
+++ b/src/backend/postmaster/shell_archive.c
@@ -18,6 +18,7 @@
#include <sys/wait.h>
#include "access/xlog.h"
+#include "common/percentrepl.h"
#include "pgstat.h"
#include "postmaster/pgarch.h"
@@ -44,58 +45,20 @@ shell_archive_configured(void)
static bool
shell_archive_file(const char *file, const char *path)
{
- char xlogarchcmd[MAXPGPATH];
- char *dp;
- char *endp;
- const char *sp;
+ char *xlogarchcmd;
+ char *nativePath = NULL;
int rc;
- /*
- * construct the command to be executed
- */
- dp = xlogarchcmd;
- endp = xlogarchcmd + MAXPGPATH - 1;
- *endp = '\0';
-
- for (sp = XLogArchiveCommand; *sp; sp++)
+ if (path)
{
- if (*sp == '%')
- {
- switch (sp[1])
- {
- case 'p':
- /* %p: relative path of source file */
- sp++;
- strlcpy(dp, path, endp - dp);
- make_native_path(dp);
- dp += strlen(dp);
- break;
- case 'f':
- /* %f: filename of source file */
- sp++;
- strlcpy(dp, file, endp - dp);
- dp += strlen(dp);
- break;
- case '%':
- /* convert %% to a single % */
- sp++;
- if (dp < endp)
- *dp++ = *sp;
- break;
- default:
- /* otherwise treat the % as not special */
- if (dp < endp)
- *dp++ = *sp;
- break;
- }
- }
- else
- {
- if (dp < endp)
- *dp++ = *sp;
- }
+ nativePath = pstrdup(path);
+ make_native_path(nativePath);
}
- *dp = '\0';
+
+ xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand, "archive_command", "fp", file, nativePath);
+
+ if (nativePath)
+ pfree(nativePath);
ereport(DEBUG3,
(errmsg_internal("executing archive command \"%s\"",
@@ -155,6 +118,8 @@ shell_archive_file(const char *file, const char *path)
return false;
}
+ pfree(xlogarchcmd);
+
elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
return true;
}
diff --git a/src/common/Makefile b/src/common/Makefile
index 898701fae1c..113029bf7b9 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -65,6 +65,7 @@ OBJS_COMMON = \
kwlookup.o \
link-canary.o \
md5_common.o \
+ percentrepl.o \
pg_get_line.o \
pg_lzcompress.o \
pg_prng.o \
diff --git a/src/common/archive.c b/src/common/archive.c
index 1466f67ea69..de42e914f74 100644
--- a/src/common/archive.c
+++ b/src/common/archive.c
@@ -20,7 +20,7 @@
#endif
#include "common/archive.h"
-#include "lib/stringinfo.h"
+#include "common/percentrepl.h"
/*
* BuildRestoreCommand
@@ -41,81 +41,20 @@ BuildRestoreCommand(const char *restoreCommand,
const char *xlogfname,
const char *lastRestartPointFname)
{
- StringInfoData result;
- const char *sp;
+ char *nativePath = NULL;
+ char *result;
- /*
- * Build the command to be executed.
- */
- initStringInfo(&result);
-
- for (sp = restoreCommand; *sp; sp++)
+ if (xlogpath)
{
- if (*sp == '%')
- {
- switch (sp[1])
- {
- case 'p':
- {
- char *nativePath;
+ nativePath = pstrdup(xlogpath);
+ make_native_path(nativePath);
+ }
- /* %p: relative path of target file */
- if (xlogpath == NULL)
- {
- pfree(result.data);
- return NULL;
- }
- sp++;
+ result = replace_percent_placeholders(restoreCommand, "restore_command", "frp",
+ xlogfname, lastRestartPointFname, nativePath);
- /*
- * This needs to use a placeholder to not modify the
- * input with the conversion done via
- * make_native_path().
- */
- nativePath = pstrdup(xlogpath);
- make_native_path(nativePath);
- appendStringInfoString(&result,
- nativePath);
- pfree(nativePath);
- break;
- }
- case 'f':
- /* %f: filename of desired file */
- if (xlogfname == NULL)
- {
- pfree(result.data);
- return NULL;
- }
- sp++;
- appendStringInfoString(&result, xlogfname);
- break;
- case 'r':
- /* %r: filename of last restartpoint */
- if (lastRestartPointFname == NULL)
- {
- pfree(result.data);
- return NULL;
- }
- sp++;
- appendStringInfoString(&result,
- lastRestartPointFname);
- break;
- case '%':
- /* convert %% to a single % */
- sp++;
- appendStringInfoChar(&result, *sp);
- break;
- default:
- /* otherwise treat the % as not special */
- appendStringInfoChar(&result, *sp);
- break;
- }
- }
- else
- {
- appendStringInfoChar(&result, *sp);
- }
- }
+ if (nativePath)
+ pfree(nativePath);
- return result.data;
+ return result;
}
diff --git a/src/common/meson.build b/src/common/meson.build
index a1fc398d8e4..41bd58ebdf1 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -17,6 +17,7 @@ common_sources = files(
'kwlookup.c',
'link-canary.c',
'md5_common.c',
+ 'percentrepl.c',
'pg_get_line.c',
'pg_lzcompress.c',
'pg_prng.c',
diff --git a/src/common/percentrepl.c b/src/common/percentrepl.c
new file mode 100644
index 00000000000..d78571fec0a
--- /dev/null
+++ b/src/common/percentrepl.c
@@ -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;
+}
diff --git a/src/fe_utils/archive.c b/src/fe_utils/archive.c
index aab813c1027..eb1c930ae7f 100644
--- a/src/fe_utils/archive.c
+++ b/src/fe_utils/archive.c
@@ -48,8 +48,6 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
xlogRestoreCmd = BuildRestoreCommand(restoreCommand, xlogpath,
xlogfname, NULL);
- if (xlogRestoreCmd == NULL)
- pg_fatal("cannot use restore_command with %%r placeholder");
/*
* Execute restore_command, which should copy the missing file from
diff --git a/src/include/common/percentrepl.h b/src/include/common/percentrepl.h
new file mode 100644
index 00000000000..0efb6ecb5ba
--- /dev/null
+++ b/src/include/common/percentrepl.h
@@ -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 */