From 4cebf4059a7462b94e0ae8d0aeeb657e396d0363 Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Sat, 28 Feb 2026 23:05:09 +0530 Subject: [PATCH v53 3/3] Support DROP EXCEPT TABLE in ALTER PUBLICATION Extend ALTER PUBLICATION to support DROP EXCEPT TABLE for publications defined with FOR ALL TABLES. --- doc/src/sgml/ref/alter_publication.sgml | 17 +++++++---- src/backend/commands/publicationcmds.c | 2 +- src/backend/commands/tablecmds.c | 2 +- src/backend/parser/gram.y | 11 ++----- src/bin/psql/tab-complete.in.c | 6 ++-- src/test/regress/expected/publication.out | 28 +++++++++++------- src/test/regress/sql/publication.sql | 3 +- .../t/037_rep_changes_except_table.pl | 29 +++++++++++++++++++ 8 files changed, 69 insertions(+), 29 deletions(-) diff --git a/doc/src/sgml/ref/alter_publication.sgml b/doc/src/sgml/ref/alter_publication.sgml index 7c28391033e..da046be474b 100644 --- a/doc/src/sgml/ref/alter_publication.sgml +++ b/doc/src/sgml/ref/alter_publication.sgml @@ -41,7 +41,7 @@ ALTER PUBLICATION name RENAME TO and publication_drop_object is one of: - TABLE [ ONLY ] table_name [ * ] [, ... ] + [ EXCEPT ] TABLE [ ONLY ] table_name [ * ] [, ... ] TABLES IN SCHEMA { schema_name | CURRENT_SCHEMA } [, ... ] and table_and_columns is: @@ -63,10 +63,11 @@ ALTER PUBLICATION name RENAME TO SET clause will replace the list of except tables/tables/schemas in the publication with the specified list; the existing except tables/tables/schemas that were present in the publication - will be removed. The ADD and DROP - clauses will add and remove one or more tables/schemas from the publication. - Note that adding tables/schemas to a publication that is already subscribed - to will require an + will be removed. The ADD clauses will add one or more + tables/schemas to the publication. The DROP clauses + will remove one or more except tables/tables/schemas from the publication. + Note that adding tables/schemas or dropping except tables to a publication + that is already subscribed to will require an ALTER SUBSCRIPTION ... REFRESH PUBLICATION action on the subscribing side in order to become effective. Note also that @@ -235,6 +236,12 @@ ALTER PUBLICATION mypublication SET TABLE users (user_id, firstname, lastname), Replace the publication's EXCEPT table list: ALTER PUBLICATION mypublication SET EXCEPT TABLE users, departments; + + + + Remove tables from the publication's EXCEPT table list: + +ALTER PUBLICATION mypublication DROP EXCEPT TABLE users, departments; diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 166fe3b0c3d..f43edd33098 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -1562,7 +1562,7 @@ CheckAlterPublication(AlterPublicationStmt *stmt, HeapTuple tup, errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("publication \"%s\" is not defined as FOR ALL TABLES", NameStr(pubform->pubname)), - errdetail("EXCEPT Tables cannot be added to publications that are not defined as FOR ALL TABLES.")); + errdetail("EXCEPT Tables cannot be added to or dropped from publications that are not defined as FOR ALL TABLES.")); } /* diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cedbdd38570..12e0d1a2574 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -20395,7 +20395,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, RelationGetRelationName(attachrel), pubnames.data), errdetail("The publication EXCEPT clause cannot contain tables that are partitions."), - errhint("Modify the publication's EXCEPT clause using ALTER PUBLICATION ... SET EXCEPT TABLE before attaching the table.")); + errhint("Modify the publication's EXCEPT clause using ALTER PUBLICATION ... SET EXCEPT TABLE or DROP EXCEPT TABLE before attaching the table.")); } list_free(exceptpuboids); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0544908fcb9..37dece5ed98 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11032,7 +11032,7 @@ AlterPublicationStmt: if (has_except_table) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), - errmsg("EXCEPT TABLE clause allowed only for SET clause")); + errmsg("EXCEPT TABLE clause allowed only for SET/DROP clause")); n->action = AP_AddObjects; $$ = (Node *) n; @@ -11049,18 +11049,11 @@ AlterPublicationStmt: } | ALTER PUBLICATION name DROP pub_obj_list { - bool has_except_table = false; AlterPublicationStmt *n = makeNode(AlterPublicationStmt); n->pubname = $3; n->pubobjects = $5; - has_except_table = preprocess_pubobj_list(n->pubobjects, - yyscanner); - if (has_except_table) - ereport(ERROR, - errcode(ERRCODE_SYNTAX_ERROR), - errmsg("EXCEPT TABLE clause allowed only for SET clause")); - + preprocess_pubobj_list(n->pubobjects, yyscanner); n->action = AP_DropObjects; $$ = (Node *) n; } diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index f45f3b76523..9ab11971c1d 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2317,8 +2317,10 @@ match_previous_words(int pattern_id, COMPLETE_WITH(","); /* ALTER PUBLICATION DROP */ else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP")) - COMPLETE_WITH("TABLES IN SCHEMA", "TABLE"); - /* ALTER PUBLICATION SET */ + COMPLETE_WITH("EXCEPT", "TABLES IN SCHEMA", "TABLE"); + else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP", "EXCEPT")) + COMPLETE_WITH("TABLE"); + /* ALTER PUBLICATION SET */ else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET")) COMPLETE_WITH("(", "EXCEPT", "TABLES IN SCHEMA", "TABLE"); else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET", "EXCEPT")) diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 3c70c51ddec..1b23116db28 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -128,14 +128,15 @@ Tables from schemas: -- fail - can't add an EXCEPT TABLE to 'FOR TABLE' publication ALTER PUBLICATION testpub_fortable ADD EXCEPT TABLE testpub_tbl1; -ERROR: EXCEPT TABLE clause allowed only for SET clause +ERROR: EXCEPT TABLE clause allowed only for SET/DROP clause -- fail - can't drop an EXCEPT TABLE from 'FOR TABLE' publication ALTER PUBLICATION testpub_fortable DROP EXCEPT TABLE testpub_tbl1; -ERROR: EXCEPT TABLE clause allowed only for SET clause +ERROR: publication "testpub_fortable" is not defined as FOR ALL TABLES +DETAIL: EXCEPT Tables cannot be added to or dropped from publications that are not defined as FOR ALL TABLES. -- fail - can't set an EXCEPT TABLE to 'FOR TABLE' publication ALTER PUBLICATION testpub_fortable SET EXCEPT TABLE testpub_tbl1; ERROR: publication "testpub_fortable" is not defined as FOR ALL TABLES -DETAIL: EXCEPT Tables cannot be added to publications that are not defined as FOR ALL TABLES. +DETAIL: EXCEPT Tables cannot be added to or dropped from publications that are not defined as FOR ALL TABLES. SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_forschema FOR TABLES IN SCHEMA pub_test; -- should be able to create publication with schema and table of the same @@ -225,14 +226,15 @@ Not-null constraints: -- fail - can't add an EXCEPT TABLE to schema publication ALTER PUBLICATION testpub_forschema ADD EXCEPT TABLE pub_test.testpub_nopk; -ERROR: EXCEPT TABLE clause allowed only for SET clause +ERROR: EXCEPT TABLE clause allowed only for SET/DROP clause -- fail - can't drop an EXCEPT TABLE from schema publication ALTER PUBLICATION testpub_forschema DROP EXCEPT TABLE pub_test.testpub_nopk; -ERROR: EXCEPT TABLE clause allowed only for SET clause +ERROR: publication "testpub_forschema" is not defined as FOR ALL TABLES +DETAIL: EXCEPT Tables cannot be added to or dropped from publications that are not defined as FOR ALL TABLES. -- fail - can't set an EXCEPT TABLE to schema publication ALTER PUBLICATION testpub_forschema SET EXCEPT TABLE pub_test.testpub_nopk; ERROR: publication "testpub_forschema" is not defined as FOR ALL TABLES -DETAIL: EXCEPT Tables cannot be added to publications that are not defined as FOR ALL TABLES. +DETAIL: EXCEPT Tables cannot be added to or dropped from publications that are not defined as FOR ALL TABLES. --------------------------------------------- -- EXCEPT TABLE tests for normal tables --------------------------------------------- @@ -285,12 +287,18 @@ ALTER PUBLICATION testpub_foralltables_excepttable SET EXCEPT TABLE testpub_tbl1 Except tables: "public.testpub_tbl1" --- fail - Dropping EXCEPT table is not supported. +-- Drop table from the EXCEPT list of a FOR ALL TABLES publication. ALTER PUBLICATION testpub_foralltables_excepttable DROP EXCEPT TABLE testpub_tbl1; -ERROR: EXCEPT TABLE clause allowed only for SET clause +\dRp+ testpub_foralltables_excepttable + Publication testpub_foralltables_excepttable + Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+---------- + regress_publication_user | t | f | t | t | t | t | none | f +(1 row) + -- fail - Adding EXCEPT table is not supported. ALTER PUBLICATION testpub_foralltables_excepttable ADD EXCEPT TABLE testpub_tbl1; -ERROR: EXCEPT TABLE clause allowed only for SET clause +ERROR: EXCEPT TABLE clause allowed only for SET/DROP clause RESET client_min_messages; DROP TABLE testpub_tbl2; DROP PUBLICATION testpub_foralltables, testpub_fortable, testpub_forschema, testpub_for_tbl_schema, testpub_foralltables_excepttable, testpub_foralltables_excepttable1; @@ -380,7 +388,7 @@ CREATE TABLE tab_main (a int) PARTITION BY RANGE(a); ALTER TABLE tab_main ATTACH PARTITION testpub_root FOR VALUES FROM (0) TO (200); ERROR: cannot attach table "testpub_root" as partition because it is referenced in publication "'testpub8'" EXCEPT clause DETAIL: The publication EXCEPT clause cannot contain tables that are partitions. -HINT: Modify the publication's EXCEPT clause using ALTER PUBLICATION ... SET EXCEPT TABLE before attaching the table. +HINT: Modify the publication's EXCEPT clause using ALTER PUBLICATION ... SET EXCEPT TABLE or DROP EXCEPT TABLE before attaching the table. RESET client_min_messages; DROP TABLE testpub_root, testpub_part1, testpub_part2, tab_main; DROP PUBLICATION testpub8; diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 957b2d183f9..a62fa0ac84d 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -138,8 +138,9 @@ CREATE PUBLICATION testpub_foralltables_excepttable1 FOR ALL TABLES EXCEPT TABLE ALTER PUBLICATION testpub_foralltables_excepttable SET EXCEPT TABLE testpub_tbl1; \dRp+ testpub_foralltables_excepttable --- fail - Dropping EXCEPT table is not supported. +-- Drop table from the EXCEPT list of a FOR ALL TABLES publication. ALTER PUBLICATION testpub_foralltables_excepttable DROP EXCEPT TABLE testpub_tbl1; +\dRp+ testpub_foralltables_excepttable -- fail - Adding EXCEPT table is not supported. ALTER PUBLICATION testpub_foralltables_excepttable ADD EXCEPT TABLE testpub_tbl1; diff --git a/src/test/subscription/t/037_rep_changes_except_table.pl b/src/test/subscription/t/037_rep_changes_except_table.pl index 786799ed260..a0c15184bd3 100644 --- a/src/test/subscription/t/037_rep_changes_except_table.pl +++ b/src/test/subscription/t/037_rep_changes_except_table.pl @@ -166,6 +166,35 @@ $result = $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM sch1.child1"); is($result, qq(10), 'check replicated inserts on subscriber'); +# Remove sch1.tab1 from the publication's EXCEPT list so that it becomes part +# of the ALL TABLES publication. +$node_publisher->safe_psql('postgres', + "ALTER PUBLICATION tap_pub_schema DROP EXCEPT TABLE sch1.tab1, sch1.parent, only sch1.parent1"); + +# Refresh the subscription so the subscriber picks up the updated +# publication definition and initiates table synchronization. +$node_subscriber->safe_psql('postgres', + "ALTER SUBSCRIPTION tap_sub_schema REFRESH PUBLICATION"); + +# Wait for initial table sync to finish +$node_subscriber->wait_for_subscription_sync($node_publisher, + 'tap_sub_schema'); + +$result = + $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM sch1.tab1"); +is($result, qq(20), 'check replicated inserts on subscriber'); + +# Insert additional rows on the publisher after synchronization. +$node_publisher->safe_psql('postgres', + "INSERT INTO sch1.tab1 VALUES(generate_series(21,30))"); + +$node_publisher->wait_for_catchup('tap_sub_schema'); + +# Verify that the new inserts are also replicated. +$result = + $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM sch1.tab1"); +is($result, qq(30), 'check replicated inserts on subscriber'); + # cleanup $node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION tap_sub_schema"); $node_publisher->safe_psql( -- 2.43.0