diff --git a/doc/src/sgml/ref/alter_subscription.sgml b/doc/src/sgml/ref/alter_subscription.sgml
index ec6d5f1dcf6..33c78aef748 100644
--- a/doc/src/sgml/ref/alter_subscription.sgml
+++ b/doc/src/sgml/ref/alter_subscription.sgml
@@ -53,6 +53,13 @@ ALTER SUBSCRIPTION name RENAME TO <
to alter the owner, you must be able to SET ROLE to the
new owning role. If the subscription has
password_required=false, only superusers can modify it.
+ If the subscription uses a foreign server, the new owner must have
+ USAGE privilege on the foreign server, a user mapping
+ for the new owner or for PUBLIC must exist, and the
+ connection string generated for the new owner must be valid. If the new
+ owner is not a superuser and the subscription has
+ password_required=true, the generated connection string
+ must include a password.
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index de62ec49514..c05f7c5de4c 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -2746,11 +2746,12 @@ AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
/*
* If the subscription uses a server, check that the new owner has USAGE
- * privileges on the server and that a user mapping exists. Note: does not
- * re-check the resulting connection string.
+ * privileges on the server, that a user mapping exists, and that the
+ * resulting connection string is valid for the new owner.
*/
if (OidIsValid(form->subserver))
{
+ char *conninfo;
ForeignServer *server = GetForeignServer(form->subserver);
aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, newOwnerId, ACL_USAGE);
@@ -2763,6 +2764,15 @@ AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
/* make sure a user mapping exists */
GetUserMapping(newOwnerId, server->serverid);
+
+ conninfo = ForeignServerConnectionString(newOwnerId, server);
+
+ /* Load the library providing us libpq calls. */
+ load_file("libpqwalreceiver", false);
+ /* Check the connection info string. */
+ walrcv_check_conninfo(conninfo,
+ form->subpasswordrequired &&
+ !superuser_arg(newOwnerId));
}
form->subowner = newOwnerId;
diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out
index 8dbfac66326..6d89cec1503 100644
--- a/src/test/regress/expected/subscription.out
+++ b/src/test/regress/expected/subscription.out
@@ -9,6 +9,10 @@ CREATE FUNCTION test_fdw_connection(oid, oid, internal)
RETURNS text
AS :'regresslib', 'test_fdw_connection'
LANGUAGE C;
+CREATE FUNCTION test_fdw_connection_no_password(oid, oid, internal)
+ RETURNS text
+ AS :'regresslib', 'test_fdw_connection_no_password'
+ LANGUAGE C;
CREATE ROLE regress_subscription_user LOGIN SUPERUSER;
CREATE ROLE regress_subscription_user2;
CREATE ROLE regress_subscription_user3 IN ROLE pg_create_subscription;
@@ -189,6 +193,18 @@ CREATE SUBSCRIPTION regress_testsub6 SERVER test_server
WARNING: subscription was created, but is not connected
HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and alter the subscription to refresh publications.
RESET SESSION AUTHORIZATION;
+GRANT USAGE ON FOREIGN SERVER test_server TO regress_subscription_user2;
+CREATE USER MAPPING FOR regress_subscription_user2 SERVER test_server OPTIONS(user 'foo');
+ALTER FOREIGN DATA WRAPPER test_fdw CONNECTION test_fdw_connection_no_password;
+WARNING: changing the foreign-data wrapper connection function can cause the options for dependent objects to become invalid
+-- fail, new owner's generated conninfo must satisfy password_required
+ALTER SUBSCRIPTION regress_testsub6 OWNER TO regress_subscription_user2;
+ERROR: password is required
+DETAIL: Non-superusers must provide a password in the connection string.
+ALTER FOREIGN DATA WRAPPER test_fdw CONNECTION test_fdw_connection;
+WARNING: changing the foreign-data wrapper connection function can cause the options for dependent objects to become invalid
+DROP USER MAPPING FOR regress_subscription_user2 SERVER test_server;
+REVOKE USAGE ON FOREIGN SERVER test_server FROM regress_subscription_user2;
REVOKE USAGE ON FOREIGN SERVER test_server FROM regress_subscription_user3;
SET SESSION AUTHORIZATION regress_subscription_user3;
-- ok, lacks USAGE on test_server, but replacing connection anyway
@@ -231,6 +247,7 @@ HINT: Use DROP ... CASCADE to drop the dependent objects too.
ALTER FOREIGN DATA WRAPPER test_fdw NO CONNECTION;
WARNING: removing the foreign-data wrapper connection function will cause dependent subscriptions to fail
DROP FUNCTION test_fdw_connection(oid, oid, internal);
+DROP FUNCTION test_fdw_connection_no_password(oid, oid, internal);
DROP FOREIGN DATA WRAPPER test_fdw;
-- fail - invalid connection string during ALTER
ALTER SUBSCRIPTION regress_testsub CONNECTION 'foobar';
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 90bb2a7e881..3cc3756a81a 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -742,6 +742,15 @@ test_fdw_connection(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P(cstring_to_text("dbname=regress_doesnotexist user=doesnotexist password=secret"));
}
+PG_FUNCTION_INFO_V1(test_fdw_connection_no_password);
+Datum
+test_fdw_connection_no_password(PG_FUNCTION_ARGS)
+{
+ /* Ensure the test fails if no valid user mapping exists. */
+ GetUserMapping(PG_GETARG_OID(0), PG_GETARG_OID(1));
+ PG_RETURN_TEXT_P(cstring_to_text("dbname=regress_doesnotexist user=doesnotexist"));
+}
+
PG_FUNCTION_INFO_V1(is_catalog_text_unique_index_oid);
Datum
is_catalog_text_unique_index_oid(PG_FUNCTION_ARGS)
diff --git a/src/test/regress/sql/subscription.sql b/src/test/regress/sql/subscription.sql
index 05533d66675..cfee0b41224 100644
--- a/src/test/regress/sql/subscription.sql
+++ b/src/test/regress/sql/subscription.sql
@@ -12,6 +12,10 @@ CREATE FUNCTION test_fdw_connection(oid, oid, internal)
RETURNS text
AS :'regresslib', 'test_fdw_connection'
LANGUAGE C;
+CREATE FUNCTION test_fdw_connection_no_password(oid, oid, internal)
+ RETURNS text
+ AS :'regresslib', 'test_fdw_connection_no_password'
+ LANGUAGE C;
CREATE ROLE regress_subscription_user LOGIN SUPERUSER;
CREATE ROLE regress_subscription_user2;
@@ -136,6 +140,17 @@ CREATE SUBSCRIPTION regress_testsub6 SERVER test_server
PUBLICATION testpub WITH (slot_name = 'dummy', connect = false);
RESET SESSION AUTHORIZATION;
+GRANT USAGE ON FOREIGN SERVER test_server TO regress_subscription_user2;
+CREATE USER MAPPING FOR regress_subscription_user2 SERVER test_server OPTIONS(user 'foo');
+ALTER FOREIGN DATA WRAPPER test_fdw CONNECTION test_fdw_connection_no_password;
+
+-- fail, new owner's generated conninfo must satisfy password_required
+ALTER SUBSCRIPTION regress_testsub6 OWNER TO regress_subscription_user2;
+
+ALTER FOREIGN DATA WRAPPER test_fdw CONNECTION test_fdw_connection;
+DROP USER MAPPING FOR regress_subscription_user2 SERVER test_server;
+REVOKE USAGE ON FOREIGN SERVER test_server FROM regress_subscription_user2;
+
REVOKE USAGE ON FOREIGN SERVER test_server FROM regress_subscription_user3;
SET SESSION AUTHORIZATION regress_subscription_user3;
@@ -182,6 +197,7 @@ DROP FUNCTION test_fdw_connection(oid, oid, internal);
ALTER FOREIGN DATA WRAPPER test_fdw NO CONNECTION;
DROP FUNCTION test_fdw_connection(oid, oid, internal);
+DROP FUNCTION test_fdw_connection_no_password(oid, oid, internal);
DROP FOREIGN DATA WRAPPER test_fdw;