On Sat, 2026-03-14 at 15:55 -0700, Jeff Davis wrote:
> Attached series including patches to address Andres's and Amit's
> comments, too.
Committed two patches.
New patch 0004: fixes missing dependencies from the FDW to the
connection function. There's a related pre-existing issue with the
dependency from the FDW to the handler function, which I will post as a
separate backportable bugfix.
I'd still like to find a good way to add pg_dump tests. The only idea I
have now is to build the test function into core postgres (without
pg_proc entry), which might be worthwhile.
Regards,
Jeff Davis
From 2939a576908866394795460ac509cea369280b2b Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email protected]>
Date: Thu, 12 Mar 2026 18:04:35 -0700
Subject: [PATCH v23 1/4] Temp context for maybe_reread_subscription().
Move temp context from ForeignServerConnectionString() to
maybe_reread_subscription(), so that it prevents more
invalidation-related leaks. Remove PG_TRY()/PG_FINALLY() from
ForeignServerConnectionString().
Suggested-by: Andres Freund <[email protected]>
Discussion: https://postgr.es/m/xvdjrdqnpap3uq7owbaox3r7p5gf7sv62aaqf2ju3vb6yglatr%40kvvwhoudrlxq
---
src/backend/catalog/pg_subscription.c | 14 -----
src/backend/foreign/foreign.c | 66 +++++++-----------------
src/backend/replication/logical/worker.c | 30 +++++++++--
src/include/catalog/pg_subscription.h | 1 -
4 files changed, 43 insertions(+), 68 deletions(-)
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index 3673d4f0bc1..ca053c152cf 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -216,20 +216,6 @@ CountDBSubscriptions(Oid dbid)
return nsubs;
}
-/*
- * Free memory allocated by subscription struct.
- */
-void
-FreeSubscription(Subscription *sub)
-{
- pfree(sub->name);
- pfree(sub->conninfo);
- if (sub->slotname)
- pfree(sub->slotname);
- list_free_deep(sub->publications);
- pfree(sub);
-}
-
/*
* Disable the given subscription.
*/
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 2edfac68d9b..1b53ca306a0 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -220,62 +220,32 @@ GetForeignServerByName(const char *srvname, bool missing_ok)
/*
* Retrieve connection string from server's FDW.
+ *
+ * NB: leaks into CurrentMemoryContext.
*/
char *
ForeignServerConnectionString(Oid userid, Oid serverid)
{
- MemoryContext tempContext;
- MemoryContext oldcxt;
- text *volatile connection_text = NULL;
- char *result = NULL;
-
- /*
- * GetForeignServer, GetForeignDataWrapper, and the connection function
- * itself all leak memory into CurrentMemoryContext. Switch to a temporary
- * context for easy cleanup.
- */
- tempContext = AllocSetContextCreate(CurrentMemoryContext,
- "FDWConnectionContext",
- ALLOCSET_SMALL_SIZES);
-
- oldcxt = MemoryContextSwitchTo(tempContext);
-
- PG_TRY();
- {
- ForeignServer *server;
- ForeignDataWrapper *fdw;
- Datum connection_datum;
-
- server = GetForeignServer(serverid);
- fdw = GetForeignDataWrapper(server->fdwid);
-
- if (!OidIsValid(fdw->fdwconnection))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("foreign data wrapper \"%s\" does not support subscription connections",
- fdw->fdwname),
- errdetail("Foreign data wrapper must be defined with CONNECTION specified.")));
-
-
- connection_datum = OidFunctionCall3(fdw->fdwconnection,
- ObjectIdGetDatum(userid),
- ObjectIdGetDatum(serverid),
- PointerGetDatum(NULL));
+ ForeignServer *server;
+ ForeignDataWrapper *fdw;
+ Datum connection_datum;
- connection_text = DatumGetTextPP(connection_datum);
- }
- PG_FINALLY();
- {
- MemoryContextSwitchTo(oldcxt);
+ server = GetForeignServer(serverid);
+ fdw = GetForeignDataWrapper(server->fdwid);
- if (connection_text)
- result = text_to_cstring((text *) connection_text);
+ if (!OidIsValid(fdw->fdwconnection))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("foreign data wrapper \"%s\" does not support subscription connections",
+ fdw->fdwname),
+ errdetail("Foreign data wrapper must be defined with CONNECTION specified.")));
- MemoryContextDelete(tempContext);
- }
- PG_END_TRY();
+ connection_datum = OidFunctionCall3(fdw->fdwconnection,
+ ObjectIdGetDatum(userid),
+ ObjectIdGetDatum(serverid),
+ PointerGetDatum(NULL));
- return result;
+ return text_to_cstring(DatumGetTextPP(connection_datum));
}
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 2d7708805a6..a8256a54a97 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -481,6 +481,7 @@ static MemoryContext LogicalStreamingContext = NULL;
WalReceiverConn *LogRepWorkerWalRcvConn = NULL;
Subscription *MySubscription = NULL;
+static MemoryContext MySubscriptionCtx = NULL;
static bool MySubscriptionValid = false;
static List *on_commit_wakeup_workers_subids = NIL;
@@ -5044,6 +5045,7 @@ void
maybe_reread_subscription(void)
{
MemoryContext oldctx;
+ MemoryContext newctx;
Subscription *newsub;
bool started_tx = false;
@@ -5058,8 +5060,15 @@ maybe_reread_subscription(void)
started_tx = true;
}
- /* Ensure allocations in permanent context. */
- oldctx = MemoryContextSwitchTo(ApplyContext);
+ newctx = AllocSetContextCreate(ApplyContext,
+ "Subscription Context",
+ ALLOCSET_SMALL_SIZES);
+
+ /*
+ * GetSubscription() leaks a number of small allocations, so use a
+ * subcontext for each call.
+ */
+ oldctx = MemoryContextSwitchTo(newctx);
newsub = GetSubscription(MyLogicalRepWorker->subid, true, true);
@@ -5151,7 +5160,8 @@ maybe_reread_subscription(void)
}
/* Clean old subscription info and switch to new one. */
- FreeSubscription(MySubscription);
+ MemoryContextDelete(MySubscriptionCtx);
+ MySubscriptionCtx = newctx;
MySubscription = newsub;
MemoryContextSwitchTo(oldctx);
@@ -5796,12 +5806,19 @@ InitializeLogRepWorker(void)
*/
SetConfigOption("search_path", "", PGC_SUSET, PGC_S_OVERRIDE);
- /* Load the subscription into persistent memory context. */
ApplyContext = AllocSetContextCreate(TopMemoryContext,
"ApplyContext",
ALLOCSET_DEFAULT_SIZES);
+
+ /*
+ * GetSubscription() leaks a number of small allocations, so use a
+ * subcontext for each call.
+ */
+ MySubscriptionCtx = AllocSetContextCreate(ApplyContext,
+ "Subscription Context",
+ ALLOCSET_SMALL_SIZES);
+
StartTransactionCommand();
- oldctx = MemoryContextSwitchTo(ApplyContext);
/*
* Lock the subscription to prevent it from being concurrently dropped,
@@ -5810,7 +5827,10 @@ InitializeLogRepWorker(void)
*/
LockSharedObject(SubscriptionRelationId, MyLogicalRepWorker->subid, 0,
AccessShareLock);
+
+ oldctx = MemoryContextSwitchTo(MySubscriptionCtx);
MySubscription = GetSubscription(MyLogicalRepWorker->subid, true, true);
+
if (!MySubscription)
{
ereport(LOG,
diff --git a/src/include/catalog/pg_subscription.h b/src/include/catalog/pg_subscription.h
index 0058d9387d7..2f6f7b57698 100644
--- a/src/include/catalog/pg_subscription.h
+++ b/src/include/catalog/pg_subscription.h
@@ -212,7 +212,6 @@ typedef struct Subscription
extern Subscription *GetSubscription(Oid subid, bool missing_ok,
bool aclcheck);
-extern void FreeSubscription(Subscription *sub);
extern void DisableSubscription(Oid subid);
extern int CountDBSubscriptions(Oid dbid);
--
2.43.0
From ca756b2802b689b87323f017455bb179b691a6c6 Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email protected]>
Date: Fri, 13 Mar 2026 19:37:21 -0700
Subject: [PATCH v23 2/4] Refactor to remove ForeignServerName().
Callers either have a ForeignServer object or can readily construct
one. Also simplify ForeignServerConnectionString() by accepting a
ForeignServer rather than its OID.
Discussion: https://postgr.es/m/CAExHW5vV5znEvecX=ra2-v7ubj9-m6qvddzub78m-txbyd1...@mail.gmail.com
Suggested-by: Ashutosh Bapat <[email protected]>
---
src/backend/catalog/pg_subscription.c | 7 ++++--
src/backend/commands/subscriptioncmds.c | 20 +++++++++-------
src/backend/foreign/foreign.c | 31 ++-----------------------
src/include/foreign/foreign.h | 4 ++--
4 files changed, 20 insertions(+), 42 deletions(-)
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index ca053c152cf..d9e220172e9 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -114,6 +114,9 @@ GetSubscription(Oid subid, bool missing_ok, bool aclcheck)
if (OidIsValid(subform->subserver))
{
AclResult aclresult;
+ ForeignServer *server;
+
+ server = GetForeignServer(subform->subserver);
/* recheck ACL if requested */
if (aclcheck)
@@ -127,11 +130,11 @@ GetSubscription(Oid subid, bool missing_ok, bool aclcheck)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("subscription owner \"%s\" does not have permission on foreign server \"%s\"",
GetUserNameFromId(subform->subowner, false),
- ForeignServerName(subform->subserver))));
+ server->servername)));
}
sub->conninfo = ForeignServerConnectionString(subform->subowner,
- subform->subserver);
+ server);
}
else
{
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 724637cff5b..7375e214cb4 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -753,7 +753,7 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
GetUserMapping(owner, server->serverid);
serverid = server->serverid;
- conninfo = ForeignServerConnectionString(owner, serverid);
+ conninfo = ForeignServerConnectionString(owner, server);
}
else
{
@@ -1841,13 +1841,13 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("subscription owner \"%s\" does not have permission on foreign server \"%s\"",
GetUserNameFromId(form->subowner, false),
- ForeignServerName(new_server->serverid)));
+ new_server->servername));
/* make sure a user mapping exists */
GetUserMapping(form->subowner, new_server->serverid);
conninfo = ForeignServerConnectionString(form->subowner,
- new_server->serverid);
+ new_server);
/* Load the library providing us libpq calls. */
load_file("libpqwalreceiver", false);
@@ -2250,7 +2250,9 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
if (OidIsValid(form->subserver))
{
AclResult aclresult;
+ ForeignServer *server;
+ server = GetForeignServer(form->subserver);
aclresult = object_aclcheck(ForeignServerRelationId, form->subserver,
form->subowner, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
@@ -2263,12 +2265,12 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
*/
err = psprintf(_("subscription owner \"%s\" does not have permission on foreign server \"%s\""),
GetUserNameFromId(form->subowner, false),
- ForeignServerName(form->subserver));
+ server->servername);
conninfo = NULL;
}
else
conninfo = ForeignServerConnectionString(form->subowner,
- form->subserver);
+ server);
}
else
{
@@ -2593,18 +2595,18 @@ AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
*/
if (OidIsValid(form->subserver))
{
- Oid serverid = form->subserver;
+ ForeignServer *server = GetForeignServer(form->subserver);
- aclresult = object_aclcheck(ForeignServerRelationId, serverid, newOwnerId, ACL_USAGE);
+ aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, newOwnerId, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
ereport(ERROR,
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("new subscription owner \"%s\" does not have permission on foreign server \"%s\"",
GetUserNameFromId(newOwnerId, false),
- ForeignServerName(serverid)));
+ server->servername));
/* make sure a user mapping exists */
- GetUserMapping(newOwnerId, serverid);
+ GetUserMapping(newOwnerId, server->serverid);
}
form->subowner = newOwnerId;
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 1b53ca306a0..005282f17f6 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -178,31 +178,6 @@ GetForeignServerExtended(Oid serverid, bits16 flags)
}
-/*
- * ForeignServerName - get name of foreign server.
- */
-char *
-ForeignServerName(Oid serverid)
-{
- Form_pg_foreign_server serverform;
- char *servername;
- HeapTuple tp;
-
- tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
-
- if (!HeapTupleIsValid(tp))
- elog(ERROR, "cache lookup failed for foreign server %u", serverid);
-
- serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
-
- servername = pstrdup(NameStr(serverform->srvname));
-
- ReleaseSysCache(tp);
-
- return servername;
-}
-
-
/*
* GetForeignServerByName - look up the foreign server definition by name.
*/
@@ -224,13 +199,11 @@ GetForeignServerByName(const char *srvname, bool missing_ok)
* NB: leaks into CurrentMemoryContext.
*/
char *
-ForeignServerConnectionString(Oid userid, Oid serverid)
+ForeignServerConnectionString(Oid userid, ForeignServer *server)
{
- ForeignServer *server;
ForeignDataWrapper *fdw;
Datum connection_datum;
- server = GetForeignServer(serverid);
fdw = GetForeignDataWrapper(server->fdwid);
if (!OidIsValid(fdw->fdwconnection))
@@ -242,7 +215,7 @@ ForeignServerConnectionString(Oid userid, Oid serverid)
connection_datum = OidFunctionCall3(fdw->fdwconnection,
ObjectIdGetDatum(userid),
- ObjectIdGetDatum(serverid),
+ ObjectIdGetDatum(server->serverid),
PointerGetDatum(NULL));
return text_to_cstring(DatumGetTextPP(connection_datum));
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index 65ed9a7f987..564c3cc1b7f 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -66,12 +66,12 @@ typedef struct ForeignTable
extern ForeignServer *GetForeignServer(Oid serverid);
-extern char *ForeignServerName(Oid serverid);
extern ForeignServer *GetForeignServerExtended(Oid serverid,
bits16 flags);
extern ForeignServer *GetForeignServerByName(const char *srvname,
bool missing_ok);
-extern char *ForeignServerConnectionString(Oid userid, Oid serverid);
+extern char *ForeignServerConnectionString(Oid userid,
+ ForeignServer *server);
extern UserMapping *GetUserMapping(Oid userid, Oid serverid);
extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
extern ForeignDataWrapper *GetForeignDataWrapperExtended(Oid fdwid,
--
2.43.0
From 92ce5061780886ddf22398d829e047bb27b135a8 Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email protected]>
Date: Sat, 14 Mar 2026 15:07:52 -0700
Subject: [PATCH v23 3/4] Add pg_dump tests related to CREATE SUBSCRIPTION ...
SERVER.
Suggested-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/CAExHW5vV5znEvecX=ra2-v7ubj9-m6qvddzub78m-txbyd1...@mail.gmail.com
---
src/bin/pg_dump/t/002_pg_dump.pl | 49 ++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 051a3d8ea3d..c0cbdd4c65e 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2846,6 +2846,40 @@ my %tests = (
like => { %full_runs, section_pre_data => 1, },
},
+ 'CREATE FUNCTION public.test_fdw_connection(oid, oid, internal)' => {
+ create_order => 37,
+ create_sql => "CREATE FUNCTION public.test_fdw_connection(oid, oid, internal) RETURNS text AS '\$libdir/regress', 'test_fdw_connection' LANGUAGE C;",
+ regexp => qr/^
+ \QCREATE FUNCTION public.test_fdw_connection(oid, oid, internal) \E
+ \QRETURNS text\E
+ \n\s+\QLANGUAGE c\E
+ \n\s+AS\ \'\$
+ \Qlibdir\/regress', 'test_fdw_connection';\E
+ /xm,
+ like => { %full_runs, section_pre_data => 1, },
+ },
+
+ 'CREATE FOREIGN DATA WRAPPER test_fdw CONNECTION public.test_fdw_connection' => {
+ create_order => 38,
+ create_sql => 'CREATE FOREIGN DATA WRAPPER test_fdw CONNECTION public.test_fdw_connection;',
+ regexp => qr/CREATE FOREIGN DATA WRAPPER test_fdw CONNECTION public.test_fdw_connection;/m,
+ like => { %full_runs, section_pre_data => 1, },
+ },
+
+ 'CREATE SERVER s2 FOREIGN DATA WRAPPER test_fdw' => {
+ create_order => 39,
+ create_sql => 'CREATE SERVER s2 FOREIGN DATA WRAPPER test_fdw;',
+ regexp => qr/CREATE SERVER s2 FOREIGN DATA WRAPPER test_fdw;/m,
+ like => { %full_runs, section_pre_data => 1, },
+ },
+
+ 'CREATE USER MAPPING FOR public SERVER s2' => {
+ create_order => 40,
+ create_sql => 'CREATE USER MAPPING FOR public SERVER s2;',
+ regexp => qr/CREATE USER MAPPING FOR public SERVER s2;/m,
+ like => { %full_runs, section_pre_data => 1, },
+ },
+
'CREATE FOREIGN TABLE dump_test.foreign_table SERVER s1' => {
create_order => 88,
create_sql =>
@@ -3287,6 +3321,21 @@ my %tests = (
},
},
+ 'CREATE SUBSCRIPTION sub4 SERVER s2' => {
+ create_order => 50,
+ create_sql => 'CREATE SUBSCRIPTION sub4
+ SERVER s2 PUBLICATION pub1
+ WITH (connect = false, slot_name = NONE, origin = any, streaming = on);',
+ regexp => qr/^
+ \QCREATE SUBSCRIPTION sub4 SERVER s2 PUBLICATION pub1 WITH (connect = false, slot_name = NONE, streaming = on);\E
+ /xm,
+ like => { %full_runs, section_post_data => 1, },
+ unlike => {
+ no_subscriptions => 1,
+ no_subscriptions_restore => 1,
+ },
+ },
+
# Regardless of whether the table or schema is excluded, publications must
# still be dumped, as excluded objects do not apply to publications. We
--
2.43.0
From 9540cc9f76178f71e2d4f7ca9621064281207131 Mon Sep 17 00:00:00 2001
From: Jeff Davis <[email protected]>
Date: Wed, 18 Mar 2026 10:31:38 -0700
Subject: [PATCH v23 4/4] Add dependency entry for FDW connection function.
Missed in commit 8185bb5347.
Catalog version bump.
---
src/backend/commands/foreigncmds.c | 40 +++++++++++++++++++++-
src/include/catalog/catversion.h | 2 +-
src/test/regress/expected/subscription.out | 9 +++++
src/test/regress/sql/subscription.sql | 8 +++++
4 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 45681235782..61ee25b345d 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -707,6 +707,14 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ if (OidIsValid(fdwconnection))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = fdwconnection;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
/* dependency on extension */
@@ -814,6 +822,28 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
{
repl_val[Anum_pg_foreign_data_wrapper_fdwconnection - 1] = ObjectIdGetDatum(fdwconnection);
repl_repl[Anum_pg_foreign_data_wrapper_fdwconnection - 1] = true;
+
+ /*
+ * If the connection function is changed, behavior of dependent
+ * subscriptions can change. If NO CONNECTION, dependent
+ * subscriptions will fail.
+ */
+ if (OidIsValid(fdwForm->fdwconnection))
+ {
+ if (OidIsValid(fdwconnection))
+ ereport(WARNING,
+ (errmsg("changing the foreign-data wrapper connection function can cause "
+ "the options for dependent objects to become invalid")));
+ else
+ ereport(WARNING,
+ (errmsg("removing the foreign-data wrapper connection function will cause "
+ "dependent subscriptions to fail")));
+ }
+ }
+ else
+ {
+ /* connection function unchanged */
+ fdwconnection = fdwForm->fdwconnection;
}
/*
@@ -854,7 +884,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
/* Update function dependencies if we changed them */
- if (handler_given || validator_given)
+ if (handler_given || validator_given || connection_given)
{
ObjectAddress referenced;
@@ -884,6 +914,14 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ if (OidIsValid(fdwconnection))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = fdwconnection;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 55a8fbbd509..a4f0d02af9c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202603171
+#define CATALOG_VERSION_NO 202603181
#endif
diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out
index f57f359127b..7e3cabdb93f 100644
--- a/src/test/regress/expected/subscription.out
+++ b/src/test/regress/expected/subscription.out
@@ -185,6 +185,15 @@ DROP SUBSCRIPTION regress_testsub6;
SET SESSION AUTHORIZATION regress_subscription_user;
REVOKE CREATE ON DATABASE REGRESSION FROM regress_subscription_user3;
DROP SERVER test_server;
+-- fail, FDW is dependent
+DROP FUNCTION test_fdw_connection(oid, oid, internal);
+ERROR: cannot drop function test_fdw_connection(oid,oid,internal) because other objects depend on it
+DETAIL: foreign-data wrapper test_fdw depends on function test_fdw_connection(oid,oid,internal)
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- warn
+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 FOREIGN DATA WRAPPER test_fdw;
-- fail - invalid connection string during ALTER
ALTER SUBSCRIPTION regress_testsub CONNECTION 'foobar';
diff --git a/src/test/regress/sql/subscription.sql b/src/test/regress/sql/subscription.sql
index a642b368183..6c3d9632e8a 100644
--- a/src/test/regress/sql/subscription.sql
+++ b/src/test/regress/sql/subscription.sql
@@ -138,6 +138,14 @@ SET SESSION AUTHORIZATION regress_subscription_user;
REVOKE CREATE ON DATABASE REGRESSION FROM regress_subscription_user3;
DROP SERVER test_server;
+
+-- fail, FDW is dependent
+DROP FUNCTION test_fdw_connection(oid, oid, internal);
+-- warn
+ALTER FOREIGN DATA WRAPPER test_fdw NO CONNECTION;
+
+DROP FUNCTION test_fdw_connection(oid, oid, internal);
+
DROP FOREIGN DATA WRAPPER test_fdw;
-- fail - invalid connection string during ALTER
--
2.43.0