From 410f54a852de6e7d431f46ad6386906ee75ac986 Mon Sep 17 00:00:00 2001 From: Hayato Kuroda Date: Thu, 23 Jul 2026 11:46:40 +0900 Subject: [PATCH 2/2] Fix pg_upgrade failure with subscriptions using foreign servers. When a subscription refers to a foreign server, pg_dump may emit the subscription before the foreign server privileges have been restored. If the subscription owner is not allowed to use that foreign server at that point, restoring the subscription ownership can fail during pg_upgrade. Restore subscription ownership as a separate TOC entry and schedule it after ACL entries. This lets foreign server GRANT commands run before the ALTER SUBSCRIPTION ... OWNER TO command. --- src/bin/pg_dump/pg_backup_archiver.c | 16 ++++++++++++- src/bin/pg_dump/pg_dump.c | 36 +++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index d7da3fc4325..bb077522a54 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -3096,7 +3096,12 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH) /* If it's a subscription, maybe ignore it */ if (ropt->no_subscriptions && (strcmp(te->desc, "SUBSCRIPTION") == 0 || - strcmp(te->desc, "SUBSCRIPTION TABLE") == 0)) + strcmp(te->desc, "SUBSCRIPTION TABLE") == 0 || + strcmp(te->desc, "SUBSCRIPTION OWNER") == 0)) + return 0; + + /* If it's a subscription owner, maybe ignore it */ + if (ropt->noOwner && strcmp(te->desc, "SUBSCRIPTION OWNER") == 0) return 0; /* Ignore it if section is not to be dumped/restored */ @@ -3400,6 +3405,15 @@ _tocEntryRestorePass(TocEntry *te) te->section == SECTION_POST_DATA) return RESTORE_PASS_POST_ACL; + /* + * ALTER SUBSCRIPTION OWNER TO command must be RESTORE_PASS_POST_ACL. The + * subscription may not be owned by the superuser and may depend on the + * foreign server. GRANT statement for the foreign server must be executed + * before the alternation. + */ + if (strcmp(te->desc, "SUBSCRIPTION OWNER") == 0) + return RESTORE_PASS_POST_ACL; + /* All else can be handled in the main pass. */ return RESTORE_PASS_MAIN; } diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4948e6d80c7..70dcd945826 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -5648,14 +5648,48 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo) } if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) + { + const char *owner; + + /* + * If the subscription refers to a foreign server, we must execute the + * ALTER SUBSCRIPTION OWNER TO command separately. If a non-superuser + * owns the subscription and the user does not own the foreign server, + * ALTER SUBSCRIPTION OWNER TO will be executed before the GRANT ... ON + * FOREIGN SERVER; this causes a failure in the restore phase. To + * address the issue, the ALTER SUBSCRIPTION command can be handled as + * a separate TOC entry and restored after ACL commands. + */ + owner = subinfo->subservername ? NULL : subinfo->rolname; + ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId, ARCHIVE_OPTS(.tag = subinfo->dobj.name, - .owner = subinfo->rolname, + .owner = owner, .description = "SUBSCRIPTION", .section = SECTION_POST_DATA, .createStmt = query->data, .dropStmt = delq->data)); + if (subinfo->subservername) + { + PQExpBuffer ownerq = createPQExpBuffer(); + appendPQExpBuffer(ownerq, + "ALTER SUBSCRIPTION %s OWNER TO %s;\n", + qsubname, + fmtId(subinfo->rolname)); + + ArchiveEntry(fout, nilCatalogId, createDumpId(), + ARCHIVE_OPTS(.tag = subinfo->dobj.name, + .description = "SUBSCRIPTION OWNER", + .section = SECTION_POST_DATA, + .createStmt = ownerq->data, + .deps = &subinfo->dobj.dumpId, + .nDeps = 1)); + + destroyPQExpBuffer(ownerq); + } + } + if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT) dumpComment(fout, "SUBSCRIPTION", qsubname, NULL, subinfo->rolname, -- 2.52.0