From cf6c42de2558b86bcee54c0b629385b8141f3054 Mon Sep 17 00:00:00 2001
From: "Chao Li (Evan)" <lic@highgo.com>
Date: Wed, 5 Aug 2026 15:11:13 +0800
Subject: [PATCH v1] pg_createsubscriber: Allow duplicate subscription names

Subscription names are database-local, so the same name can be used in
different databases.  However, pg_createsubscriber rejected duplicate
--subscription values unconditionally.

Allow duplicate subscription names when distinct replication slot names
are specified explicitly.  Continue to reject them when
--replication-slot is omitted, because subscription names are then reused
as replication slot names, which must be unique within a cluster.

Add tests and update the documentation.

Suggested-by: Amit Kapila <amit.kapila16@gmail.com>
Author: Chao Li <lic@highgo.com>
Reviewed-by:
Discussion: https://postgr.es/m/
---
 doc/src/sgml/ref/pg_createsubscriber.sgml     |  4 +++-
 src/bin/pg_basebackup/pg_createsubscriber.c   | 19 +++++++++------
 .../t/040_pg_createsubscriber.pl              | 24 +++++++++++++++----
 3 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index 193f60628bc..60d7c8e2a2d 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -362,7 +362,9 @@ PostgreSQL documentation
        is reported.  The order of the multiple subscription name switches must
        match the order of database switches.  If this option is not specified,
        a generated name is assigned to the subscription name. This option cannot
-       be used together with <option>--all</option>.
+       be used together with <option>--all</option>.  The same subscription name
+       can be used in different databases only when distinct replication slot
+       names are specified with <option>--replication-slot</option>.
       </para>
      </listitem>
     </varlistentry>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 20b354aed56..dcf8422dd92 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -2262,6 +2262,7 @@ main(int argc, char **argv)
 
 	int			c;
 	int			option_index;
+	bool		duplicate_sub_name = false;
 
 	char	   *pub_base_conninfo;
 	char	   *sub_base_conninfo;
@@ -2394,13 +2395,10 @@ main(int argc, char **argv)
 					pg_fatal("replication slot \"%s\" specified more than once for --replication-slot", optarg);
 				break;
 			case 4:
-				if (!simple_string_list_member(&opt.sub_names, optarg))
-				{
-					simple_string_list_append(&opt.sub_names, optarg);
-					num_subs++;
-				}
-				else
-					pg_fatal("subscription \"%s\" specified more than once for --subscription", optarg);
+				if (simple_string_list_member(&opt.sub_names, optarg))
+					duplicate_sub_name = true;
+				simple_string_list_append(&opt.sub_names, optarg);
+				num_subs++;
 				break;
 			case 5:
 				if (!simple_string_list_member(&opt.objecttypes_to_clean, optarg))
@@ -2587,6 +2585,13 @@ main(int argc, char **argv)
 							num_replslots, num_dbs);
 		exit(1);
 	}
+	if (duplicate_sub_name && num_replslots == 0)
+	{
+		pg_log_error("duplicate subscription names require distinct replication slot names");
+		pg_log_error_detail("When replication slot names are not specified, subscription names are used, but replication slot names must be unique within a cluster.");
+		pg_log_error_hint("Specify a unique --replication-slot name for each database.");
+		exit(1);
+	}
 
 	/* Verify the object types specified for removal from the subscriber */
 	for (SimpleStringListCell *cell = opt.objecttypes_to_clean.head; cell; cell = cell->next)
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 9252d1c3c5c..82fb4394bea 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -91,6 +91,19 @@ command_fails(
 		'--database' => 'pg2',
 	],
 	'wrong number of subscription names');
+command_fails_like(
+	[
+		'pg_createsubscriber',
+		'--verbose',
+		'--pgdata' => $datadir,
+		'--publisher-server' => 'port=5432',
+		'--subscription' => 'bar1',
+		'--subscription' => 'bar1',
+		'--database' => 'pg1',
+		'--database' => 'pg2',
+	],
+	qr/duplicate subscription names require distinct replication slot names/,
+	'duplicate subscription names without replication slot names');
 command_fails(
 	[
 		'pg_createsubscriber',
@@ -334,8 +347,9 @@ is($node_s->safe_psql($db1, "SELECT COUNT(*) FROM pg_publication"),
 
 $node_s->stop;
 
-# dry run mode on node S. Use the same publication name for different
-# databases, since publication names are database-local.
+# dry run mode on node S. Use the same publication and subscription names for
+# different databases, since both names are database-local. Distinct replication
+# slot names are required because replication slots are cluster-global.
 command_ok(
 	[
 		'pg_createsubscriber',
@@ -348,8 +362,10 @@ command_ok(
 		'--subscriber-port' => $node_s->port,
 		'--publication' => 'same_pub',
 		'--publication' => 'same_pub',
-		'--subscription' => 'sub1',
-		'--subscription' => 'sub2',
+		'--subscription' => 'same_sub',
+		'--subscription' => 'same_sub',
+		'--replication-slot' => 'slot1',
+		'--replication-slot' => 'slot2',
 		'--database' => $db1,
 		'--database' => $db2,
 		'--logdir' => $logdir,
-- 
2.50.1 (Apple Git-155)

