From be0a9d1de941e40a30c853e5940be3eaf3026232 Mon Sep 17 00:00:00 2001 From: Dilip Kumar Date: Tue, 28 Apr 2026 11:11:57 +0530 Subject: [PATCH v5 1/2] Include schema-qualified names in EXCEPT clause error messages. Previously, error messages in check_publication_add_relation() only reported the relation name when a table included in an EXCEPT clause could not be processed. This could be ambiguous in databases where the same relation name exists in multiple schemas. This patch updates these error messages to use schema-qualified names, improving the clarity of error reporting for CREATE PUBLICATION and ALTER PUBLICATION commands using EXCEPT clauses. A later patch will extend the same improvement to non-EXCEPT cases. --- src/backend/catalog/pg_publication.c | 29 +++++++++++++++++++---- src/backend/utils/adt/ruleutils.c | 12 ++-------- src/backend/utils/cache/lsyscache.c | 20 ++++++++++++++++ src/include/utils/lsyscache.h | 1 + src/test/regress/expected/publication.out | 2 +- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c index a43d385c605..6a59804e7f6 100644 --- a/src/backend/catalog/pg_publication.c +++ b/src/backend/catalog/pg_publication.c @@ -48,6 +48,18 @@ typedef struct * table. */ } published_rel; +static char *get_relation_qualified_name(Relation rel); + +/* + * Get a palloc'd string containing the schema-qualified name of the relation. + */ +static char * +get_relation_qualified_name(Relation rel) +{ + return get_qualified_relname(RelationGetNamespace(rel), + RelationGetRelationName(rel)); +} + /* * Check if relation can be in given publication and throws appropriate * error if not. @@ -56,18 +68,25 @@ static void check_publication_add_relation(PublicationRelInfo *pri) { Relation targetrel = pri->relation; + const char *relname; const char *errormsg; if (pri->except) + { + relname = get_relation_qualified_name(targetrel); errormsg = gettext_noop("cannot specify relation \"%s\" in the publication EXCEPT clause"); + } else + { + relname = RelationGetRelationName(targetrel); errormsg = gettext_noop("cannot add relation \"%s\" to publication"); + } /* If in EXCEPT clause, must be root partitioned table */ if (pri->except && targetrel->rd_rel->relispartition) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg(errormsg, RelationGetRelationName(targetrel)), + errmsg(errormsg, relname), errdetail("This operation is not supported for individual partitions."))); /* Must be a regular or partitioned table */ @@ -75,26 +94,26 @@ check_publication_add_relation(PublicationRelInfo *pri) RelationGetForm(targetrel)->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg(errormsg, RelationGetRelationName(targetrel)), + errmsg(errormsg, relname), errdetail_relkind_not_supported(RelationGetForm(targetrel)->relkind))); /* Can't be system table */ if (IsCatalogRelation(targetrel)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg(errormsg, RelationGetRelationName(targetrel)), + errmsg(errormsg, relname), errdetail("This operation is not supported for system tables."))); /* UNLOGGED and TEMP relations cannot be part of publication. */ if (targetrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg(errormsg, RelationGetRelationName(targetrel)), + errmsg(errormsg, relname), errdetail("This operation is not supported for temporary tables."))); else if (targetrel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg(errormsg, RelationGetRelationName(targetrel)), + errmsg(errormsg, relname), errdetail("This operation is not supported for unlogged tables."))); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 75b77bb39f1..71c207c2cd0 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -13876,23 +13876,15 @@ generate_qualified_relation_name(Oid relid) { HeapTuple tp; Form_pg_class reltup; - char *relname; - char *nspname; char *result; tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for relation %u", relid); reltup = (Form_pg_class) GETSTRUCT(tp); - relname = NameStr(reltup->relname); - - nspname = get_namespace_name_or_temp(reltup->relnamespace); - if (!nspname) - elog(ERROR, "cache lookup failed for namespace %u", - reltup->relnamespace); - - result = quote_qualified_identifier(nspname, relname); + result = get_qualified_relname(reltup->relnamespace, + NameStr(reltup->relname)); ReleaseSysCache(tp); return result; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 3de10d4df7e..01eefe0ff45 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3628,6 +3628,26 @@ get_namespace_name_or_temp(Oid nspid) return get_namespace_name(nspid); } +/* + * get_qualified_relname + * Get a palloc'd string containing the schema-qualified name of the relation + * for the given namespace ID and relation name. + */ +char * +get_qualified_relname(Oid nspid, char *relname) +{ + char *nspname; + char *result; + + nspname = get_namespace_name_or_temp(nspid); + if (!nspname) + elog(ERROR, "cache lookup failed for namespace %u", nspid); + + result = quote_qualified_identifier(nspname, relname); + + return result; +} + /* ---------- PG_RANGE CACHES ---------- */ /* diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 8d5e92e07be..261fa6a2c42 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -200,6 +200,7 @@ extern bool get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple, extern void free_attstatsslot(AttStatsSlot *sslot); extern char *get_namespace_name(Oid nspid); extern char *get_namespace_name_or_temp(Oid nspid); +extern char *get_qualified_relname(Oid nspid, char *relname); extern Oid get_range_subtype(Oid rangeOid); extern Oid get_range_collation(Oid rangeOid); extern Oid get_range_constructor2(Oid rangeOid); diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 0345f6c5e47..29e54b214a0 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -458,7 +458,7 @@ Excluded from publications: Number of partitions: 1 (Use \d+ to list them.) CREATE PUBLICATION testpub9 FOR ALL TABLES EXCEPT (TABLE testpub_part1); -ERROR: cannot specify relation "testpub_part1" in the publication EXCEPT clause +ERROR: cannot specify relation "public.testpub_part1" in the publication EXCEPT clause DETAIL: This operation is not supported for individual partitions. CREATE TABLE tab_main (a int) PARTITION BY RANGE(a); -- Attaching a partition is not allowed if the partitioned table appears in a -- 2.43.0