Re: Common function for percent placeholder replacement

2023-01-11 Thread Peter Eisentraut

On 11.01.23 19:54, Nathan Bossart wrote:

On Wed, Jan 11, 2023 at 11:09:01AM +0100, Peter Eisentraut wrote:

committed with that fixed


While rebasing my recovery modules patch set, I noticed a couple of small
things that might be worth cleaning up.


committed, thanks





Re: Common function for percent placeholder replacement

2023-01-11 Thread Nathan Bossart
On Wed, Jan 11, 2023 at 11:09:01AM +0100, Peter Eisentraut wrote:
> committed with that fixed

While rebasing my recovery modules patch set, I noticed a couple of small
things that might be worth cleaning up.

-- 
Nathan Bossart
Amazon Web Services: https://aws.amazon.com
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index f911e8c3a6..fcc87ff44f 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -154,9 +154,6 @@ RestoreArchivedFile(char *path, const char *xlogfname,
 	xlogRestoreCmd = BuildRestoreCommand(recoveryRestoreCommand,
 		 xlogpath, xlogfname,
 		 lastRestartPointFname);
-	if (xlogRestoreCmd == NULL)
-		elog(ERROR, "could not build restore command \"%s\"",
-			 recoveryRestoreCommand);
 
 	ereport(DEBUG3,
 			(errmsg_internal("executing restore command \"%s\"",
diff --git a/src/common/archive.c b/src/common/archive.c
index de42e914f7..641a58ee88 100644
--- a/src/common/archive.c
+++ b/src/common/archive.c
@@ -33,7 +33,7 @@
  * The result is a palloc'd string for the restore command built.  The
  * caller is responsible for freeing it.  If any of the required arguments
  * is NULL and that the corresponding alias is found in the command given
- * by the caller, then NULL is returned.
+ * by the caller, then an error is thrown.
  */
 char *
 BuildRestoreCommand(const char *restoreCommand,


Re: Common function for percent placeholder replacement

2023-01-11 Thread Peter Eisentraut

On 09.01.23 18:53, Nathan Bossart wrote:

+   nativePath = pstrdup(path);
+   make_native_path(nativePath);



+   nativePath = pstrdup(xlogpath);
+   make_native_path(nativePath);


Should these be freed?


committed with that fixed





Re: Common function for percent placeholder replacement

2023-01-09 Thread Nathan Bossart
On Mon, Jan 09, 2023 at 09:36:12AM +0100, Peter Eisentraut wrote:
> On 04.01.23 01:37, Nathan Bossart wrote:
>> On Tue, Dec 20, 2022 at 06:30:40AM +0100, Peter Eisentraut wrote:
>> > + * A value may be NULL.  If the corresponding placeholder is found in the
>> > + * input string, the whole function returns NULL.
>> 
>> This appears to be carried over from BuildRestoreCommand(), and AFAICT it
>> is only necessary because pg_rewind doesn't support %r in restore_command.
>> IMHO this behavior is counterintuitive and possibly error-prone and should
>> result in an ERROR instead.  Since pg_rewind is the only special case, it
>> could independently check for %r before building the command.
> 
> Yeah, this annoyed me, too.  I have now changed it so that a NULL "value" is
> the same as an unsupported placeholder.  This preserves the existing
> behavior.
> 
> (Having pg_rewind check for %r itself would probably require replicating
> most of the string processing logic (consider something like "%%r"), so it
> didn't seem appealing.)

Sounds good to me.

> + nativePath = pstrdup(path);
> + make_native_path(nativePath);

> + nativePath = pstrdup(xlogpath);
> + make_native_path(nativePath);

Should these be freed?

-- 
Nathan Bossart
Amazon Web Services: https://aws.amazon.com




Re: Common function for percent placeholder replacement

2023-01-09 Thread Peter Eisentraut

On 04.01.23 01:37, Nathan Bossart wrote:

In general, +1.

On Tue, Dec 20, 2022 at 06:30:40AM +0100, Peter Eisentraut wrote:

(Still need to think about Robert's comment about lack of error context.)


Would adding the name of the GUC be sufficient?

ereport(ERROR,
(errmsg("could not build %s", guc_name),
 errdetail("string ends unexpectedly after escape character 
\"%%\"")));


done

The errors now basically look like an invalid GUC value.


+ * A value may be NULL.  If the corresponding placeholder is found in the
+ * input string, the whole function returns NULL.


This appears to be carried over from BuildRestoreCommand(), and AFAICT it
is only necessary because pg_rewind doesn't support %r in restore_command.
IMHO this behavior is counterintuitive and possibly error-prone and should
result in an ERROR instead.  Since pg_rewind is the only special case, it
could independently check for %r before building the command.


Yeah, this annoyed me, too.  I have now changed it so that a NULL 
"value" is the same as an unsupported placeholder.  This preserves the 
existing behavior.


(Having pg_rewind check for %r itself would probably require replicating 
most of the string processing logic (consider something like "%%r"), so 
it didn't seem appealing.)
From 3975b1296174218c5e25e2b02bca88cb3c26ee63 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Mon, 9 Jan 2023 09:03:30 +0100
Subject: [PATCH v4] 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.

Discussion: 
https://www.postgresql.org/message-id/flat/5238bbed-0b01-83a6-d4b2-7eb0562a054e%40enterprisedb.com
---
 .../basebackup_to_shell/basebackup_to_shell.c |  56 +--
 src/backend/access/transam/xlogarchive.c  |  45 +-
 src/backend/libpq/be-secure-common.c  |  38 +
 src/backend/postmaster/shell_archive.c|  58 ++--
 src/common/Makefile   |   1 +
 src/common/archive.c  |  81 +--
 src/common/meson.build|   1 +
 src/common/percentrepl.c  | 137 ++
 src/fe_utils/archive.c|   2 -
 src/include/common/percentrepl.h  |  18 +++
 10 files changed, 190 insertions(+), 247 deletions(-)
 create mode 100644 src/common/percentrepl.c
 create mode 100644 src/include/common/percentrepl.h

diff --git a/contrib/basebackup_to_shell/basebackup_to_shell.c 
b/contrib/basebackup_to_shell/basebackup_to_shell.c
index 8d583550b5..29f5069d42 100644
--- a/contrib/basebackup_to_shell/basebackup_to_shell.c
+++ b/contrib/basebackup_to_shell/basebackup_to_shell.c
@@ -12,6 +12,7 @@
 
 #include "access/xact.h"
 #include "backup/basebackup_target.h"
+#include "common/percentrepl.h"
 #include "miscadmin.h"
 #include "storage/fd.h"
 #include "utils/acl.h"
@@ -208,59 +209,8 @@ static char *
 shell_construct_command(const char *base_command, const char *filename,
const char *target_detail)
 {
-   StringInfoData buf;
-   const char *c;
-
-   initStringInfo();
-   for (c = base_command; *c != '\0'; ++c)
-   {
-   /* Anything other than '%' is copied verbatim. */
-   if (*c != '%')
-   {
-   appendStringInfoChar(, *c);
-   continue;
-   }
-
-   /* Any time we see '%' we eat the following character as well. 
*/
-   ++c;
-
-   /*
-* The following character determines what we insert here, or 
may
-* cause us to throw an error.
-*/
-   if (*c == '%')
-   {
-   /* '%%' is replaced by a single '%' */
-   appendStringInfoChar(, '%');
-   }
-   else if (*c == 'f')
-   {
-   /* '%f' is replaced by the filename */
-   appendStringInfoString(, filename);
-   }
-   else if (*c == 'd')
-   {
-   /* '%d' is replaced by the target detail */
-   appendStringInfoString(, target_detail);
-   }
-   else if (*c == '\0')
-   {
-   /* Incomplete escape sequence, expected a character 
afterward */
-   ereport(ERROR,
-   errcode(ERRCODE_SYNTAX_ERROR),
-   errmsg("shell command ends unexpectedly 
after escape character \"%%\""));
-   

Re: Common function for percent placeholder replacement

2023-01-03 Thread Nathan Bossart
In general, +1.

On Tue, Dec 20, 2022 at 06:30:40AM +0100, Peter Eisentraut wrote:
> (Still need to think about Robert's comment about lack of error context.)

Would adding the name of the GUC be sufficient?

ereport(ERROR,
(errmsg("could not build %s", guc_name),
 errdetail("string ends unexpectedly after escape 
character \"%%\"")));

> + * A value may be NULL.  If the corresponding placeholder is found in the
> + * input string, the whole function returns NULL.

This appears to be carried over from BuildRestoreCommand(), and AFAICT it
is only necessary because pg_rewind doesn't support %r in restore_command.
IMHO this behavior is counterintuitive and possibly error-prone and should
result in an ERROR instead.  Since pg_rewind is the only special case, it
could independently check for %r before building the command.

-- 
Nathan Bossart
Amazon Web Services: https://aws.amazon.com




Re: Common function for percent placeholder replacement

2022-12-20 Thread Corey Huinker
>
> How about this new one with variable arguments?


I like this a lot, but I also see merit in Alvaro's PERCENT_OPT variadic,
which at least avoids the two lists getting out of sync.

Initially, I was going to ask that we have shell-quote-safe equivalents of
whatever fixed parameters we baked in, but this allows the caller to do
that as needed. It seems we could now just copy quote_identifier and strip
out the keyword checks to get the desired effect. Has anyone else had a
need for quote-safe args in the shell commands?


Re: Common function for percent placeholder replacement

2022-12-19 Thread Peter Eisentraut

On 19.12.22 10:51, Alvaro Herrera wrote:

I think the new one is not great.  I wish we could do something more
straightforward, maybe like

   replace_percent_placeholders(base_command,
PERCENT_OPT("f", filename),
PERCENT_OPT("d", target_detail));

Is there a performance disadvantage to a variadic implementation?
Alternatively, have all these macro calls form an array.


How about this new one with variable arguments?

(Still need to think about Robert's comment about lack of error context.)
From 76dbfd291313e610aadb0e01eb46f1b0113f2c0f Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Tue, 20 Dec 2022 06:27:42 +0100
Subject: [PATCH v3] 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.

Discussion: 
https://www.postgresql.org/message-id/flat/5238bbed-0b01-83a6-d4b2-7eb0562a054e%40enterprisedb.com
---
 .../basebackup_to_shell/basebackup_to_shell.c |  55 +---
 src/backend/access/transam/xlogarchive.c  |  45 +--
 src/backend/libpq/be-secure-common.c  |  38 ++
 src/backend/postmaster/shell_archive.c|  58 ++---
 src/common/Makefile   |   1 +
 src/common/archive.c  |  81 +---
 src/common/meson.build|   1 +
 src/common/percentrepl.c  | 123 ++
 src/include/common/percentrepl.h  |  18 +++
 9 files changed, 175 insertions(+), 245 deletions(-)
 create mode 100644 src/common/percentrepl.c
 create mode 100644 src/include/common/percentrepl.h

diff --git a/contrib/basebackup_to_shell/basebackup_to_shell.c 
b/contrib/basebackup_to_shell/basebackup_to_shell.c
index bdaf67a4c8..0dceab65f8 100644
--- a/contrib/basebackup_to_shell/basebackup_to_shell.c
+++ b/contrib/basebackup_to_shell/basebackup_to_shell.c
@@ -12,6 +12,7 @@
 
 #include "access/xact.h"
 #include "backup/basebackup_target.h"
+#include "common/percentrepl.h"
 #include "miscadmin.h"
 #include "storage/fd.h"
 #include "utils/acl.h"
@@ -208,59 +209,7 @@ static char *
 shell_construct_command(const char *base_command, const char *filename,
const char *target_detail)
 {
-   StringInfoData buf;
-   const char *c;
-
-   initStringInfo();
-   for (c = base_command; *c != '\0'; ++c)
-   {
-   /* Anything other than '%' is copied verbatim. */
-   if (*c != '%')
-   {
-   appendStringInfoChar(, *c);
-   continue;
-   }
-
-   /* Any time we see '%' we eat the following character as well. 
*/
-   ++c;
-
-   /*
-* The following character determines what we insert here, or 
may
-* cause us to throw an error.
-*/
-   if (*c == '%')
-   {
-   /* '%%' is replaced by a single '%' */
-   appendStringInfoChar(, '%');
-   }
-   else if (*c == 'f')
-   {
-   /* '%f' is replaced by the filename */
-   appendStringInfoString(, filename);
-   }
-   else if (*c == 'd')
-   {
-   /* '%d' is replaced by the target detail */
-   appendStringInfoString(, target_detail);
-   }
-   else if (*c == '\0')
-   {
-   /* Incomplete escape sequence, expected a character 
afterward */
-   ereport(ERROR,
-   errcode(ERRCODE_SYNTAX_ERROR),
-   errmsg("shell command ends unexpectedly 
after escape character \"%%\""));
-   }
-   else
-   {
-   /* Unknown escape sequence */
-   ereport(ERROR,
-   errcode(ERRCODE_SYNTAX_ERROR),
-   errmsg("shell command contains 
unexpected escape sequence \"%c\"",
-  *c));
-   }
-   }
-
-   return buf.data;
+   return replace_percent_placeholders(base_command, "df", target_detail, 
filename);
 }
 
 /*
diff --git a/src/backend/access/transam/xlogarchive.c 
b/src/backend/access/transam/xlogarchive.c
index e2b7176f2f..7b32f67331 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 

Re: Common function for percent placeholder replacement

2022-12-19 Thread Robert Haas
On Mon, Dec 19, 2022 at 3:13 AM Peter Eisentraut
 wrote:
> I agree.  Here is an updated patch with the error checking included.

Nice, but I think something in the error report needs to indicate what
caused the problem exactly. As coded, I think the user would have to
guess which GUC caused the problem. For basebackup_to_shell that might
not be too hard since you would have to try to initiate a backup to a
shell target to trigger the error, but for something that happens at
server start, you don't want to have to go search all of
postgresql.conf for possible causes.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: Common function for percent placeholder replacement

2022-12-19 Thread Alvaro Herrera
On 2022-Dec-19, Peter Eisentraut wrote:

> On 14.12.22 18:05, Justin Pryzby wrote:
> > On Wed, Dec 14, 2022 at 08:31:02AM +0100, Peter Eisentraut wrote:
> > > + return replace_percent_placeholders(base_command, "df", (const char 
> > > *[]){target_detail, filename});
> > 
> > This is a "compound literal", which I gather is required by C99.
> > 
> > But I don't think that's currently being exercised, so I wonder if it's
> > going to break some BF members.
> 
> We already use this, for example in pg_dump.

Yeah, we have this

#define ARCHIVE_OPTS(...) &(ArchiveOpts){__VA_ARGS__}

which we then use like this

ArchiveEntry(fout,
 dbCatId,   /* catalog ID */
 dbDumpId,  /* dump ID */
 ARCHIVE_OPTS(.tag = datname,
  .owner = dba,
  .description = "DATABASE",
  .section = SECTION_PRE_DATA,
  .createStmt = creaQry->data,
  .dropStmt = delQry->data));

I think the new one is not great.  I wish we could do something more
straightforward, maybe like

  replace_percent_placeholders(base_command,
   PERCENT_OPT("f", filename),
   PERCENT_OPT("d", target_detail));

Is there a performance disadvantage to a variadic implementation?
Alternatively, have all these macro calls form an array.

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
Subversion to GIT: the shortest path to happiness I've ever heard of
(Alexey Klyukin)




Re: Common function for percent placeholder replacement

2022-12-19 Thread Peter Eisentraut

On 14.12.22 18:05, Justin Pryzby wrote:

On Wed, Dec 14, 2022 at 08:31:02AM +0100, Peter Eisentraut wrote:

+   return replace_percent_placeholders(base_command, "df", (const char 
*[]){target_detail, filename});


This is a "compound literal", which I gather is required by C99.

But I don't think that's currently being exercised, so I wonder if it's
going to break some BF members.


We already use this, for example in pg_dump.





Re: Common function for percent placeholder replacement

2022-12-19 Thread Peter Eisentraut

On 14.12.22 17:09, Robert Haas wrote:

Well, OK, I'll tentatively cast a vote in favor of adopting
basebackup_to_shell's approach elsewhere. Or to put that in plain
English: I think that if the input appears to be malformed, it's
better to throw an error than to guess what the user meant. In the
case of basebackup_to_shell there are potentially security
ramifications to the setting involved so it seemed like a bad idea to
take a laissez faire approach. But also, just in general, if somebody
supplies an ssl_passphrase_command or archive_command with %, I don't really see why we should treat that differently
than trying to start the server with work_mem=banana.


I agree.  Here is an updated patch with the error checking included.
From 8bb85ad7ed99d0255ea82306b08ef7d343ab8edb Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Mon, 19 Dec 2022 09:04:43 +0100
Subject: [PATCH v2] 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.

Discussion: 
https://www.postgresql.org/message-id/flat/5238bbed-0b01-83a6-d4b2-7eb0562a054e%40enterprisedb.com
---
 .../basebackup_to_shell/basebackup_to_shell.c |  55 +---
 src/backend/access/transam/xlogarchive.c  |  45 +--
 src/backend/libpq/be-secure-common.c  |  38 ++
 src/backend/postmaster/shell_archive.c|  59 ++---
 src/common/Makefile   |   1 +
 src/common/archive.c  |  81 +---
 src/common/meson.build|   1 +
 src/common/percentrepl.c  | 120 ++
 src/include/common/percentrepl.h  |  18 +++
 9 files changed, 173 insertions(+), 245 deletions(-)
 create mode 100644 src/common/percentrepl.c
 create mode 100644 src/include/common/percentrepl.h

diff --git a/contrib/basebackup_to_shell/basebackup_to_shell.c 
b/contrib/basebackup_to_shell/basebackup_to_shell.c
index bdaf67a4c8..9a3d57c64b 100644
--- a/contrib/basebackup_to_shell/basebackup_to_shell.c
+++ b/contrib/basebackup_to_shell/basebackup_to_shell.c
@@ -12,6 +12,7 @@
 
 #include "access/xact.h"
 #include "backup/basebackup_target.h"
+#include "common/percentrepl.h"
 #include "miscadmin.h"
 #include "storage/fd.h"
 #include "utils/acl.h"
@@ -208,59 +209,7 @@ static char *
 shell_construct_command(const char *base_command, const char *filename,
const char *target_detail)
 {
-   StringInfoData buf;
-   const char *c;
-
-   initStringInfo();
-   for (c = base_command; *c != '\0'; ++c)
-   {
-   /* Anything other than '%' is copied verbatim. */
-   if (*c != '%')
-   {
-   appendStringInfoChar(, *c);
-   continue;
-   }
-
-   /* Any time we see '%' we eat the following character as well. 
*/
-   ++c;
-
-   /*
-* The following character determines what we insert here, or 
may
-* cause us to throw an error.
-*/
-   if (*c == '%')
-   {
-   /* '%%' is replaced by a single '%' */
-   appendStringInfoChar(, '%');
-   }
-   else if (*c == 'f')
-   {
-   /* '%f' is replaced by the filename */
-   appendStringInfoString(, filename);
-   }
-   else if (*c == 'd')
-   {
-   /* '%d' is replaced by the target detail */
-   appendStringInfoString(, target_detail);
-   }
-   else if (*c == '\0')
-   {
-   /* Incomplete escape sequence, expected a character 
afterward */
-   ereport(ERROR,
-   errcode(ERRCODE_SYNTAX_ERROR),
-   errmsg("shell command ends unexpectedly 
after escape character \"%%\""));
-   }
-   else
-   {
-   /* Unknown escape sequence */
-   ereport(ERROR,
-   errcode(ERRCODE_SYNTAX_ERROR),
-   errmsg("shell command contains 
unexpected escape sequence \"%c\"",
-  *c));
-   }
-   }
-
-   return buf.data;
+   return replace_percent_placeholders(base_command, "df", (const char 
*[]){target_detail, filename});
 }
 
 /*
diff --git a/src/backend/access/transam/xlogarchive.c 

Re: Common function for percent placeholder replacement

2022-12-14 Thread Tom Lane
Justin Pryzby  writes:
> On Wed, Dec 14, 2022 at 08:31:02AM +0100, Peter Eisentraut wrote:
>> +return replace_percent_placeholders(base_command, "df", (const char 
>> *[]){target_detail, filename});

> This is a "compound literal", which I gather is required by C99.

> But I don't think that's currently being exercised, so I wonder if it's
> going to break some BF members.

It's pretty illegible, whatever it is.  Could we maybe expend a
few more keystrokes in favor of readability?

regards, tom lane




Re: Common function for percent placeholder replacement

2022-12-14 Thread Justin Pryzby
On Wed, Dec 14, 2022 at 08:31:02AM +0100, Peter Eisentraut wrote:
> + return replace_percent_placeholders(base_command, "df", (const char 
> *[]){target_detail, filename});

This is a "compound literal", which I gather is required by C99.

But I don't think that's currently being exercised, so I wonder if it's
going to break some BF members.

-- 
Justin




Re: Common function for percent placeholder replacement

2022-12-14 Thread Robert Haas
On Wed, Dec 14, 2022 at 2:31 AM Peter Eisentraut
 wrote:
> There are a number of places where a shell command is constructed with
> percent-placeholders (like %x).  First, it's obviously cumbersome to
> have to open-code this several times.  Second, each of those pieces of
> code silently encodes some edge case behavior, such as what to do with
> unrecognized placeholders.  (I remember when I last did one of these, I
> stared very hard at the existing code instances to figure out what they
> would do.)  We now also have a newer instance in basebackup_to_shell.c
> that has different behavior in such cases.  (Maybe it's better, but it
> would be good to be explicit and consistent about this.)

Well, OK, I'll tentatively cast a vote in favor of adopting
basebackup_to_shell's approach elsewhere. Or to put that in plain
English: I think that if the input appears to be malformed, it's
better to throw an error than to guess what the user meant. In the
case of basebackup_to_shell there are potentially security
ramifications to the setting involved so it seemed like a bad idea to
take a laissez faire approach. But also, just in general, if somebody
supplies an ssl_passphrase_command or archive_command with %, I don't really see why we should treat that differently
than trying to start the server with work_mem=banana.

-- 
Robert Haas
EDB: http://www.enterprisedb.com