From 8ef6bfa89de6f49da73f4a942661f62cc6b0e536 Mon Sep 17 00:00:00 2001 From: Dilip Kumar Date: Tue, 28 Apr 2026 11:11:57 +0530 Subject: [PATCH v7] 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..3dac633d4a2 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); + +/* + * Returns a schema-qualified name of the relation. + */ +static char * +get_relation_qualified_name(Relation rel) +{ + return get_qualified_objname(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..88de5c0481c 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_objname(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..036086057d7 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_objname + * Returns a palloc'd string containing the schema-qualified name of the + * object for the given namespace ID and object name. + */ +char * +get_qualified_objname(Oid nspid, char *objname) +{ + 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, objname); + + return result; +} + /* ---------- PG_RANGE CACHES ---------- */ /* diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 8d5e92e07be..8545e67a632 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_objname(Oid nspid, char *objname); 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.52.0.windows.1