On Fri, 24 Oct 2025 at 15:08, Amit Kapila <[email protected]> wrote:
>
> On Fri, Oct 24, 2025 at 1:24 PM Hayato Kuroda (Fujitsu)
> <[email protected]> wrote:
> >
> > Dear Shlok,
> >
> > ```
> > -- Can set synchronized_standby_slots to a non-existent slot name.
> > ALTER SYSTEM SET synchronized_standby_slots='missing';
> > SELECT pg_reload_conf();
> >
> > -- Reset the GUC.
> > ALTER SYSTEM RESET synchronized_standby_slots;
> > SELECT pg_reload_conf();
> > ```
> >
> > pg_reload_conf() is called twice here but I'm not sure it is really needed.
> > ALTER SYSTEM itself can validate parameters via 
> > parse_and_validate_value(elevel=ERROR),
> > and pg_reload_conf() won't affect synchronously.
>
> I also think pg_reload_conf() won't be required here.
>
I agree that .pg_reload_conf() is not required. I have removed it in
the latest version of patches.

Thanks,
Shlok Kyal
From 789534c514a3c587c9f9d8dfb20bab1d361d0eb3 Mon Sep 17 00:00:00 2001
From: Shlok Kyal <[email protected]>
Date: Wed, 10 Sep 2025 15:08:23 +0530
Subject: [PATCH v9_REL_18] Remove the validation from the GUC check hook and
 add parsing check

The validation in check_synchronized_standby_slots cannot be run
in postmaster and hence can result inconsistent values in some
backends that inherit the value from postmaster and those
that are started newly. Also, this validation results in parallel
workers fail to with error. This causes all the commands run by
parallel workers to fail, which seems unnecesary. This validation
already happens in StandbySlotsHaveCaughtup() where this GUC is
used, so it can be removed from the GUC check. Also added a
parsing check for the slot names specified in this GUC.
---
 src/backend/replication/slot.c    | 59 +++++++++----------------------
 src/test/regress/expected/guc.out | 10 ++++++
 src/test/regress/sql/guc.sql      | 10 ++++++
 3 files changed, 36 insertions(+), 43 deletions(-)

diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index ac910f6a74a..101157ed8c9 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -2743,53 +2743,32 @@ 
GetSlotInvalidationCauseName(ReplicationSlotInvalidationCause cause)
 static bool
 validate_sync_standby_slots(char *rawname, List **elemlist)
 {
-       bool            ok;
-
        /* Verify syntax and parse string into a list of identifiers */
-       ok = SplitIdentifierString(rawname, ',', elemlist);
-
-       if (!ok)
+       if (!SplitIdentifierString(rawname, ',', elemlist))
        {
                GUC_check_errdetail("List syntax is invalid.");
+               return false;
        }
-       else if (MyProc)
+
+       /* Iterate the list to validate each slot name */
+       foreach_ptr(char, name, *elemlist)
        {
-               /*
-                * Check that each specified slot exist and is physical.
-                *
-                * Because we need an LWLock, we cannot do this on processes 
without a
-                * PGPROC, so we skip it there; but see comments in
-                * StandbySlotsHaveCaughtup() as to why that's not a problem.
-                */
-               LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
+               int                     err_code;
+               char       *err_msg = NULL;
+               char       *err_hint = NULL;
 
-               foreach_ptr(char, name, *elemlist)
+               if (!ReplicationSlotValidateNameInternal(name, &err_code, 
&err_msg,
+                                                                               
                 &err_hint))
                {
-                       ReplicationSlot *slot;
-
-                       slot = SearchNamedReplicationSlot(name, false);
-
-                       if (!slot)
-                       {
-                               GUC_check_errdetail("Replication slot \"%s\" 
does not exist.",
-                                                                       name);
-                               ok = false;
-                               break;
-                       }
-
-                       if (!SlotIsPhysical(slot))
-                       {
-                               GUC_check_errdetail("\"%s\" is not a physical 
replication slot.",
-                                                                       name);
-                               ok = false;
-                               break;
-                       }
+                       GUC_check_errcode(err_code);
+                       GUC_check_errdetail("%s", err_msg);
+                       if (err_hint != NULL)
+                               GUC_check_errhint("%s", err_hint);
+                       return false;
                }
-
-               LWLockRelease(ReplicationSlotControlLock);
        }
 
-       return ok;
+       return true;
 }
 
 /*
@@ -2947,12 +2926,6 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int 
elevel)
                /*
                 * If a slot name provided in synchronized_standby_slots does 
not
                 * exist, report a message and exit the loop.
-                *
-                * Though validate_sync_standby_slots (the GUC check_hook) 
tries to
-                * avoid this, it can nonetheless happen because the user can 
specify
-                * a nonexistent slot name before server startup. That function 
cannot
-                * validate such a slot during startup, as ReplicationSlotCtl 
is not
-                * initialized by then.  Also, the user might have dropped one 
slot.
                 */
                if (!slot)
                {
diff --git a/src/test/regress/expected/guc.out 
b/src/test/regress/expected/guc.out
index 7f9e29c765c..804bf17f360 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -913,3 +913,13 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test for GUC synchronized standby slots.
+-- Cannot set synchronized_standby_slots to an invalid slot name.
+ALTER SYSTEM SET synchronized_standby_slots='invalid*';
+ERROR:  invalid value for parameter "synchronized_standby_slots": "invalid*"
+DETAIL:  replication slot name "invalid*" contains invalid character
+HINT:  Replication slot names may only contain lower case letters, numbers, 
and the underscore character.
+-- Can set synchronized_standby_slots to a non-existent slot name.
+ALTER SYSTEM SET synchronized_standby_slots='missing';
+-- Reset the GUC.
+ALTER SYSTEM RESET synchronized_standby_slots;
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index f65f84a2632..10b790b2c5b 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -368,3 +368,13 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test for GUC synchronized standby slots.
+-- Cannot set synchronized_standby_slots to an invalid slot name.
+ALTER SYSTEM SET synchronized_standby_slots='invalid*';
+
+-- Can set synchronized_standby_slots to a non-existent slot name.
+ALTER SYSTEM SET synchronized_standby_slots='missing';
+
+-- Reset the GUC.
+ALTER SYSTEM RESET synchronized_standby_slots;
-- 
2.34.1

From c951d59cedc78261473c664e5c6b8e479fcaeec4 Mon Sep 17 00:00:00 2001
From: Shlok Kyal <[email protected]>
Date: Wed, 10 Sep 2025 15:08:23 +0530
Subject: [PATCH v9_HEAD] Remove the validation from the GUC check hook and add
 parsing check

The validation in check_synchronized_standby_slots cannot be run
in postmaster and hence can result inconsistent values in some
backends that inherit the value from postmaster and those
that are started newly. Also, this validation results in parallel
workers fail to with error. This causes all the commands run by
parallel workers to fail, which seems unnecesary. This validation
already happens in StandbySlotsHaveCaughtup() where this GUC is
used, so it can be removed from the GUC check. Also added a
parsing check for the slot names specified in this GUC.
---
 src/backend/replication/slot.c    | 59 +++++++++----------------------
 src/test/regress/expected/guc.out | 15 ++++++++
 src/test/regress/sql/guc.sql      | 13 +++++++
 3 files changed, 44 insertions(+), 43 deletions(-)

diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index a4ca363f20d..1e9f4602c69 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -2784,53 +2784,32 @@ GetSlotInvalidationCauseName(ReplicationSlotInvalidationCause cause)
 static bool
 validate_sync_standby_slots(char *rawname, List **elemlist)
 {
-	bool		ok;
-
 	/* Verify syntax and parse string into a list of identifiers */
-	ok = SplitIdentifierString(rawname, ',', elemlist);
-
-	if (!ok)
+	if (!SplitIdentifierString(rawname, ',', elemlist))
 	{
 		GUC_check_errdetail("List syntax is invalid.");
+		return false;
 	}
-	else if (MyProc)
+
+	/* Iterate the list to validate each slot name */
+	foreach_ptr(char, name, *elemlist)
 	{
-		/*
-		 * Check that each specified slot exist and is physical.
-		 *
-		 * Because we need an LWLock, we cannot do this on processes without a
-		 * PGPROC, so we skip it there; but see comments in
-		 * StandbySlotsHaveCaughtup() as to why that's not a problem.
-		 */
-		LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
+		int			err_code;
+		char	   *err_msg = NULL;
+		char	   *err_hint = NULL;
 
-		foreach_ptr(char, name, *elemlist)
+		if (!ReplicationSlotValidateNameInternal(name, false, &err_code,
+												 &err_msg, &err_hint))
 		{
-			ReplicationSlot *slot;
-
-			slot = SearchNamedReplicationSlot(name, false);
-
-			if (!slot)
-			{
-				GUC_check_errdetail("Replication slot \"%s\" does not exist.",
-									name);
-				ok = false;
-				break;
-			}
-
-			if (!SlotIsPhysical(slot))
-			{
-				GUC_check_errdetail("\"%s\" is not a physical replication slot.",
-									name);
-				ok = false;
-				break;
-			}
+			GUC_check_errcode(err_code);
+			GUC_check_errdetail("%s", err_msg);
+			if (err_hint != NULL)
+				GUC_check_errhint("%s", err_hint);
+			return false;
 		}
-
-		LWLockRelease(ReplicationSlotControlLock);
 	}
 
-	return ok;
+	return true;
 }
 
 /*
@@ -2988,12 +2967,6 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel)
 		/*
 		 * If a slot name provided in synchronized_standby_slots does not
 		 * exist, report a message and exit the loop.
-		 *
-		 * Though validate_sync_standby_slots (the GUC check_hook) tries to
-		 * avoid this, it can nonetheless happen because the user can specify
-		 * a nonexistent slot name before server startup. That function cannot
-		 * validate such a slot during startup, as ReplicationSlotCtl is not
-		 * initialized by then.  Also, the user might have dropped one slot.
 		 */
 		if (!slot)
 		{
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 7f9e29c765c..a1bf251b8f3 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -913,3 +913,18 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test for GUC synchronized standby slots.
+-- Cannot set synchronized_standby_slots to a reserved slot name.
+ALTER SYSTEM SET synchronized_standby_slots='pg_conflict_detection';
+ERROR:  invalid value for parameter "synchronized_standby_slots": "pg_conflict_detection"
+DETAIL:  replication slot name "pg_conflict_detection" is reserved
+HINT:  The name "pg_conflict_detection" is reserved for the conflict detection slot.
+-- Cannot set synchronized_standby_slots to an invalid slot name.
+ALTER SYSTEM SET synchronized_standby_slots='invalid*';
+ERROR:  invalid value for parameter "synchronized_standby_slots": "invalid*"
+DETAIL:  replication slot name "invalid*" contains invalid character
+HINT:  Replication slot names may only contain lower case letters, numbers, and the underscore character.
+-- Can set synchronized_standby_slots to a non-existent slot name.
+ALTER SYSTEM SET synchronized_standby_slots='missing';
+-- Reset the GUC.
+ALTER SYSTEM RESET synchronized_standby_slots;
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index f65f84a2632..154bba859a6 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -368,3 +368,16 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test for GUC synchronized standby slots.
+-- Cannot set synchronized_standby_slots to a reserved slot name.
+ALTER SYSTEM SET synchronized_standby_slots='pg_conflict_detection';
+
+-- Cannot set synchronized_standby_slots to an invalid slot name.
+ALTER SYSTEM SET synchronized_standby_slots='invalid*';
+
+-- Can set synchronized_standby_slots to a non-existent slot name.
+ALTER SYSTEM SET synchronized_standby_slots='missing';
+
+-- Reset the GUC.
+ALTER SYSTEM RESET synchronized_standby_slots;
-- 
2.34.1

From 9aca60e26cb22c13d6876d7868ec0e6fd3c12ff6 Mon Sep 17 00:00:00 2001
From: Shlok Kyal <[email protected]>
Date: Thu, 23 Oct 2025 11:09:47 +0530
Subject: [PATCH v9_REL_17] Remove the validation from the GUC check hook and
 add parsing check

The validation in check_synchronized_standby_slots cannot be run
in postmaster and hence can result inconsistent values in some
backends that inherit the value from postmaster and those
that are started newly. Also, this validation results in parallel
workers fail to with error. This causes all the commands run by
parallel workers to fail, which seems unnecesary. This validation
already happens in StandbySlotsHaveCaughtup() where this GUC is
used, so it can be removed from the GUC check. Also added a
parsing check for the slot names specified in this GUC.
---
 src/backend/replication/slot.c    | 59 +++++++++----------------------
 src/test/regress/expected/guc.out | 10 ++++++
 src/test/regress/sql/guc.sql      | 10 ++++++
 3 files changed, 36 insertions(+), 43 deletions(-)

diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index 80b8abde3a2..4bc2be33396 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -2468,53 +2468,32 @@ GetSlotInvalidationCause(const char 
*invalidation_reason)
 static bool
 validate_sync_standby_slots(char *rawname, List **elemlist)
 {
-       bool            ok;
-
        /* Verify syntax and parse string into a list of identifiers */
-       ok = SplitIdentifierString(rawname, ',', elemlist);
-
-       if (!ok)
+       if (!SplitIdentifierString(rawname, ',', elemlist))
        {
                GUC_check_errdetail("List syntax is invalid.");
+               return false;
        }
-       else if (MyProc)
+
+       /* Iterate the list to validate each slot name */
+       foreach_ptr(char, name, *elemlist)
        {
-               /*
-                * Check that each specified slot exist and is physical.
-                *
-                * Because we need an LWLock, we cannot do this on processes 
without a
-                * PGPROC, so we skip it there; but see comments in
-                * StandbySlotsHaveCaughtup() as to why that's not a problem.
-                */
-               LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
+               int                     err_code;
+               char       *err_msg = NULL;
+               char       *err_hint = NULL;
 
-               foreach_ptr(char, name, *elemlist)
+               if (!ReplicationSlotValidateNameInternal(name, &err_code, 
&err_msg,
+                                                                               
                 &err_hint))
                {
-                       ReplicationSlot *slot;
-
-                       slot = SearchNamedReplicationSlot(name, false);
-
-                       if (!slot)
-                       {
-                               GUC_check_errdetail("replication slot \"%s\" 
does not exist",
-                                                                       name);
-                               ok = false;
-                               break;
-                       }
-
-                       if (!SlotIsPhysical(slot))
-                       {
-                               GUC_check_errdetail("\"%s\" is not a physical 
replication slot",
-                                                                       name);
-                               ok = false;
-                               break;
-                       }
+                       GUC_check_errcode(err_code);
+                       GUC_check_errdetail("%s", err_msg);
+                       if (err_hint != NULL)
+                               GUC_check_errhint("%s", err_hint);
+                       return false;
                }
-
-               LWLockRelease(ReplicationSlotControlLock);
        }
 
-       return ok;
+       return true;
 }
 
 /*
@@ -2672,12 +2651,6 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int 
elevel)
                /*
                 * If a slot name provided in synchronized_standby_slots does 
not
                 * exist, report a message and exit the loop.
-                *
-                * Though validate_sync_standby_slots (the GUC check_hook) 
tries to
-                * avoid this, it can nonetheless happen because the user can 
specify
-                * a nonexistent slot name before server startup. That function 
cannot
-                * validate such a slot during startup, as ReplicationSlotCtl 
is not
-                * initialized by then.  Also, the user might have dropped one 
slot.
                 */
                if (!slot)
                {
diff --git a/src/test/regress/expected/guc.out 
b/src/test/regress/expected/guc.out
index 455b6d6c0ce..f03e722a441 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -888,3 +888,13 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test for GUC synchronized standby slots.
+-- Cannot set synchronized_standby_slots to an invalid slot name.
+ALTER SYSTEM SET synchronized_standby_slots='invalid*';
+ERROR:  invalid value for parameter "synchronized_standby_slots": "invalid*"
+DETAIL:  replication slot name "invalid*" contains invalid character
+HINT:  Replication slot names may only contain lower case letters, numbers, 
and the underscore character.
+-- Can set synchronized_standby_slots to a non-existent slot name.
+ALTER SYSTEM SET synchronized_standby_slots='missing';
+-- Reset the GUC.
+ALTER SYSTEM RESET synchronized_standby_slots;
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index dc79761955d..17ed240bf55 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -353,3 +353,13 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test for GUC synchronized standby slots.
+-- Cannot set synchronized_standby_slots to an invalid slot name.
+ALTER SYSTEM SET synchronized_standby_slots='invalid*';
+
+-- Can set synchronized_standby_slots to a non-existent slot name.
+ALTER SYSTEM SET synchronized_standby_slots='missing';
+
+-- Reset the GUC.
+ALTER SYSTEM RESET synchronized_standby_slots;
-- 
2.34.1

Reply via email to