From 243475c3e7887c19db73720887dd741f0ceeb532 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Thu, 30 Jul 2026 18:18:25 -0700
Subject: [PATCH v3 08/11] For subscription DDL, demote user mapping checks to
 WARNING.

The checks are useful to report to the user, but there's no reason to
raise an error. If needed while constructing conninfo, fdwconnection
will raise an error then.

Partially addresses finding 1, and addresses finding 13 in report from
the linked discussion.

Reported-by: Noah Misch <noah@leadboat.com>
Discussion: https://postgr.es/m/20260710195902.4f.noahmisch%40microsoft.com
Discussion: https://postgr.es/m/e103ae8daf74485e0c0ebde297fae735d38f54d1.camel@j-davis.com
Backpatch-through: 19
---
 src/backend/commands/subscriptioncmds.c    |  8 ++++----
 src/backend/foreign/foreign.c              | 14 +++++++++++++-
 src/include/foreign/foreign.h              |  1 +
 src/test/regress/expected/subscription.out |  8 +++-----
 src/test/regress/sql/subscription.sql      |  5 +----
 5 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 77e10d78e64..b91c07d208e 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -806,8 +806,8 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
 		if (aclresult != ACLCHECK_OK)
 			aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
 
-		/* make sure a user mapping exists */
-		GetUserMapping(owner, server->serverid);
+		/* check user mapping */
+		GetUserMappingExtended(owner, server->serverid, WARNING);
 
 		serverid = server->serverid;
 		conninfo = ForeignServerConnectionString(owner, server);
@@ -2170,8 +2170,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 								   GetUserNameFromId(form->subowner, false),
 								   new_server->servername));
 
-				/* make sure a user mapping exists */
-				GetUserMapping(form->subowner, new_server->serverid);
+				/* check user mapping */
+				GetUserMappingExtended(form->subowner, new_server->serverid, WARNING);
 
 				new_conninfo = ForeignServerConnectionString(form->subowner,
 															 new_server);
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 821d45c1e11..73343f017b3 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -230,6 +230,16 @@ ForeignServerConnectionString(Oid userid, ForeignServer *server)
  */
 UserMapping *
 GetUserMapping(Oid userid, Oid serverid)
+{
+	return GetUserMappingExtended(userid, serverid, ERROR);
+}
+
+/*
+ * Like GetUserMapping(), but allows caller to specify an elevel. If elevel is
+ * less than ERROR, returns NULL if the user mapping doesn't exist.
+ */
+UserMapping *
+GetUserMappingExtended(Oid userid, Oid serverid, int elevel)
 {
 	Datum		datum;
 	HeapTuple	tp;
@@ -252,10 +262,12 @@ GetUserMapping(Oid userid, Oid serverid)
 	{
 		ForeignServer *server = GetForeignServer(serverid);
 
-		ereport(ERROR,
+		ereport(elevel,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
 				 errmsg("user mapping not found for user \"%s\", server \"%s\"",
 						MappingUserName(userid), server->servername)));
+
+		return NULL;
 	}
 
 	um = palloc_object(UserMapping);
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index 92a55214fee..9b4532895a4 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -73,6 +73,7 @@ extern ForeignServer *GetForeignServerByName(const char *srvname,
 extern char *ForeignServerConnectionString(Oid userid,
 										   ForeignServer *server);
 extern UserMapping *GetUserMapping(Oid userid, Oid serverid);
+extern UserMapping *GetUserMappingExtended(Oid userid, Oid serverid, int elevel);
 extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
 extern ForeignDataWrapper *GetForeignDataWrapperExtended(Oid fdwid,
 														 uint16 flags);
diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out
index f67ffab1f54..e36f227129b 100644
--- a/src/test/regress/expected/subscription.out
+++ b/src/test/regress/expected/subscription.out
@@ -177,14 +177,12 @@ ERROR:  permission denied for foreign server test_server
 RESET SESSION AUTHORIZATION;
 GRANT USAGE ON FOREIGN SERVER test_server TO regress_subscription_user3;
 SET SESSION AUTHORIZATION regress_subscription_user3;
--- fail, need user mapping
-CREATE SUBSCRIPTION regress_testsub6 SERVER test_server PUBLICATION testpub WITH (slot_name = NONE, connect = false);
-ERROR:  user mapping not found for user "regress_subscription_user3", server "test_server"
-CREATE USER MAPPING FOR regress_subscription_user3 SERVER test_server OPTIONS(user 'foo', password 'secret');
--- fail, need CONNECTION clause
+-- warn, need user mapping, then fail, FDW doesn't support connections
 CREATE SUBSCRIPTION regress_testsub6 SERVER test_server PUBLICATION testpub WITH (slot_name = NONE, connect = false);
+WARNING:  user mapping not found for user "regress_subscription_user3", server "test_server"
 ERROR:  foreign data wrapper "test_fdw" does not support subscription connections
 DETAIL:  Foreign data wrapper must be defined with CONNECTION specified.
+CREATE USER MAPPING FOR regress_subscription_user3 SERVER test_server OPTIONS(user 'foo', password 'secret');
 RESET SESSION AUTHORIZATION;
 ALTER FOREIGN DATA WRAPPER test_fdw CONNECTION test_fdw_connection;
 SET SESSION AUTHORIZATION regress_subscription_user3;
diff --git a/src/test/regress/sql/subscription.sql b/src/test/regress/sql/subscription.sql
index 47e2b6ef09c..5ee13df6653 100644
--- a/src/test/regress/sql/subscription.sql
+++ b/src/test/regress/sql/subscription.sql
@@ -124,14 +124,11 @@ RESET SESSION AUTHORIZATION;
 GRANT USAGE ON FOREIGN SERVER test_server TO regress_subscription_user3;
 SET SESSION AUTHORIZATION regress_subscription_user3;
 
--- fail, need user mapping
+-- warn, need user mapping, then fail, FDW doesn't support connections
 CREATE SUBSCRIPTION regress_testsub6 SERVER test_server PUBLICATION testpub WITH (slot_name = NONE, connect = false);
 
 CREATE USER MAPPING FOR regress_subscription_user3 SERVER test_server OPTIONS(user 'foo', password 'secret');
 
--- fail, need CONNECTION clause
-CREATE SUBSCRIPTION regress_testsub6 SERVER test_server PUBLICATION testpub WITH (slot_name = NONE, connect = false);
-
 RESET SESSION AUTHORIZATION;
 ALTER FOREIGN DATA WRAPPER test_fdw CONNECTION test_fdw_connection;
 SET SESSION AUTHORIZATION regress_subscription_user3;
-- 
2.43.0

