From 8c9b04f003d4989e09149efd3c875edc31a3a5bd Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Mon, 20 Apr 2026 15:02:31 +0530 Subject: [PATCH v1] Handle EXCEPT publications when changing table to UNLOGGED When changing a table to UNLOGGED, tables that appear in publications only via EXCEPT clauses (prexcept = true) were allowed, but left stale entries in pg_publication_rel. Since UNLOGGED tables are not supported in publications, remove such entries when the table is changed to UNLOGGED, and emit a NOTICE to inform the user. --- src/backend/commands/tablecmds.c | 46 +++++++++++++++++++---- src/test/regress/expected/publication.out | 26 +++++++++++++ src/test/regress/sql/publication.sql | 16 ++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index eec09ba1ded..3cb4b51489a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -98,6 +98,7 @@ #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/catcache.h" #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" @@ -19093,13 +19094,44 @@ ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged) * Check that the table is not part of any publication when changing to * UNLOGGED, as UNLOGGED tables can't be published. */ - if (!toLogged && - GetRelationIncludedPublications(RelationGetRelid(rel)) != NIL) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot change table \"%s\" to unlogged because it is part of a publication", - RelationGetRelationName(rel)), - errdetail("Unlogged relations cannot be replicated."))); + if (!toLogged) + { + CatCList *pubrellist; + + /* Find all publications associated with the relation. */ + pubrellist = SearchSysCacheList1(PUBLICATIONRELMAP, + ObjectIdGetDatum(RelationGetRelid(rel))); + for (int i = 0; i < pubrellist->n_members; i++) + { + HeapTuple tup = &pubrellist->members[i]->tuple; + Form_pg_publication_rel pubrel = (Form_pg_publication_rel) GETSTRUCT(tup); + + if (!pubrel->prexcept) + { + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot change table \"%s\" to unlogged because it is part of a publication", + RelationGetRelationName(rel)), + errdetail("Unlogged relations cannot be replicated."))); + } + else + { + ObjectAddress obj; + char *pubname; + + pubname = get_publication_name(pubrel->prpubid, false); + + ObjectAddressSet(obj, PublicationRelRelationId, pubrel->oid); + performDeletion(&obj, DROP_CASCADE, 0); + + ereport(NOTICE, + errmsg("relation \"%s\" removed from publication \"%s\" due to being changed to UNLOGGED", + RelationGetRelationName(rel), pubname)); + } + } + + ReleaseSysCacheList(pubrellist); + } /* * Check existing foreign key constraints to preserve the invariant that diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index d028e9be866..dec7f5b24fb 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -256,6 +256,32 @@ Except publications: "testpub_foralltables_excepttable" "testpub_foralltables_excepttable1" +SET client_min_messages = 'NOTICE'; +-- Change testpub_tbl1 to UNLOGGED. This should remove the relation from the +-- publication (and effectively from the EXCEPT clause), since UNLOGGED tables +-- are not supported in publications. +ALTER TABLE testpub_tbl1 SET UNLOGGED; +NOTICE: relation "testpub_tbl1" removed from publication "testpub_foralltables_excepttable" due to being changed to UNLOGGED +NOTICE: relation "testpub_tbl1" removed from publication "testpub_foralltables_excepttable1" due to being changed to UNLOGGED +\dRp+ testpub_foralltables_excepttable1 + Publication testpub_foralltables_excepttable1 + Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- + regress_publication_user | t | f | t | t | t | t | none | f | +(1 row) + +\d testpub_tbl1 + Unlogged table "public.testpub_tbl1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+------------------------------------------ + id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) + data | text | | | +Indexes: + "testpub_tbl1_pkey" PRIMARY KEY, btree (id) + +-- Restore the table to LOGGED. +ALTER TABLE testpub_tbl1 SET LOGGED; +SET client_min_messages = 'ERROR'; -- fail - first table in the EXCEPT list should use TABLE keyword CREATE PUBLICATION testpub_foralltables_excepttable2 FOR ALL TABLES EXCEPT (testpub_tbl1, testpub_tbl2); ERROR: syntax error at or near "testpub_tbl1" diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 642e32fa098..3c8b5a3a980 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -119,6 +119,22 @@ CREATE PUBLICATION testpub_foralltables_excepttable1 FOR ALL TABLES EXCEPT (TABL -- Check that the table description shows the publications where it is listed -- in the EXCEPT clause \d testpub_tbl1 + +SET client_min_messages = 'NOTICE'; + +-- Change testpub_tbl1 to UNLOGGED. This should remove the relation from the +-- publication (and effectively from the EXCEPT clause), since UNLOGGED tables +-- are not supported in publications. +ALTER TABLE testpub_tbl1 SET UNLOGGED; +\dRp+ testpub_foralltables_excepttable1 +\d testpub_tbl1 + +-- Restore the table to LOGGED. +ALTER TABLE testpub_tbl1 SET LOGGED; + +SET client_min_messages = 'ERROR'; + + -- fail - first table in the EXCEPT list should use TABLE keyword CREATE PUBLICATION testpub_foralltables_excepttable2 FOR ALL TABLES EXCEPT (testpub_tbl1, testpub_tbl2); -- 2.43.0