From 52524a8e63a34c1aebbbdd598f9322409946151f 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 v3] 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: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: Peter Smith <smithpb2250@gmail.com>
Discussion: https://postgr.es/m/2B037930-9B7F-41D3-98B3-E50FFD94C01A@gmail.com
---
 doc/src/sgml/ref/pg_createsubscriber.sgml     |  9 ++++---
 src/bin/pg_basebackup/pg_createsubscriber.c   | 23 +++++++++++-------
 .../t/040_pg_createsubscriber.pl              | 24 +++++++++++++++----
 3 files changed, 41 insertions(+), 15 deletions(-)

diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index 193f60628bc..78a2596f72e 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -355,14 +355,17 @@ PostgreSQL documentation
      <term><option>--subscription=<replaceable class="parameter">name</replaceable></option></term>
      <listitem>
       <para>
-       The subscription name to set up the logical replication.  Multiple
+       The subscription name to set up the logical replication.  This option
+       cannot be used together with <option>--all</option>.  Multiple
        subscriptions can be specified by writing multiple
        <option>--subscription</option> switches.  The number of subscription
        names must match the number of specified databases, otherwise an error
        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>.
+       a generated name is assigned to the subscription name.  The same
+       subscription name can be used in different databases only when
+       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..5d834edbe72 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_names = 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 (!duplicate_sub_names)
+					duplicate_sub_names = simple_string_list_member(&opt.sub_names, optarg);
+				simple_string_list_append(&opt.sub_names, optarg);
+				num_subs++;
 				break;
 			case 5:
 				if (!simple_string_list_member(&opt.objecttypes_to_clean, optarg))
@@ -2580,7 +2578,16 @@ main(int argc, char **argv)
 							num_subs, num_dbs);
 		exit(1);
 	}
-	if (num_replslots > 0 && num_replslots != num_dbs)
+	if (num_replslots == 0)
+	{
+		if (duplicate_sub_names)
+		{
+			pg_log_error("duplicate subscription names require replication slot names");
+			pg_log_error_hint("Specify --replication-slot for each database.");
+			exit(1);
+		}
+	}
+	else if (num_replslots != num_dbs)
 	{
 		pg_log_error("wrong number of replication slot names specified");
 		pg_log_error_detail("The number of specified replication slot names (%d) must match the number of specified database names (%d).",
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 9252d1c3c5c..7b381637574 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 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. Replication slot
+# names are specified 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)

