From d484cbc3cb74c333ee6f6d184ddbcecce927bbd5 Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Fri, 6 Mar 2026 12:57:32 +0530 Subject: [PATCH v58 2/2] 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 | 18 +++++++++++----- 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 | 8 +++++-- src/test/regress/expected/publication.out | 26 +++++++++++++++++------ src/test/regress/sql/publication.sql | 7 +++++- src/test/subscription/t/037_except.pl | 21 ++++++++++++++++++ 8 files changed, 70 insertions(+), 25 deletions(-) diff --git a/doc/src/sgml/ref/alter_publication.sgml b/doc/src/sgml/ref/alter_publication.sgml index d13e128f21e..b27d92b42e5 100644 --- a/doc/src/sgml/ref/alter_publication.sgml +++ b/doc/src/sgml/ref/alter_publication.sgml @@ -41,7 +41,8 @@ ALTER PUBLICATION name RENAME TO and publication_drop_object is one of: - TABLE [ ONLY ] table_name [ * ] [, ... ] + TABLE [ ONLY ] ( table_name [ * ] [, ... ] ) + EXCEPT TABLE [ ONLY ] ( table_name [ * ] [, ... ] ) TABLES IN SCHEMA { schema_name | CURRENT_SCHEMA } [, ... ] and table_and_columns is: @@ -63,10 +64,11 @@ ALTER PUBLICATION name RENAME TO SET clause will replace the list of tables/except tables/schemas in the publication with the specified list; the existing tables/except 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 clause will add one or more + tables/schemas to the publication. The DROP clause will + remove one or more tables/except 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 +237,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 ab77f11d7a4..e64d66ce9fe 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -20401,7 +20401,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.")); + errhint("Modify the publication's EXCEPT clause using ALTER PUBLICATION ... SET/DROP EXCEPT TABLE.")); } list_free(exceptpuboids); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 7e99824b8b1..71f2e27816e 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11031,7 +11031,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; @@ -11048,18 +11048,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 411e4d0cb7f..5f1fcd43b23 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2317,8 +2317,12 @@ 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 TABLE (", "TABLES IN SCHEMA", "TABLE"); + else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP", "EXCEPT")) + COMPLETE_WITH("TABLE ("); + else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP", "EXCEPT", "TABLE")) + COMPLETE_WITH("("); + /* ALTER PUBLICATION SET */ else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET")) COMPLETE_WITH("(", "EXCEPT TABLE (", "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 a50d66a2bd5..3347b59cfa2 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -126,10 +126,14 @@ ALTER PUBLICATION testpub_fortable SET TABLES IN SCHEMA pub_test; Tables from schemas: "pub_test" +-- fail - can't drop an EXCEPT TABLE from 'FOR TABLE' publication +ALTER PUBLICATION testpub_fortable DROP EXCEPT TABLE (testpub_tbl1); +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 @@ -217,10 +221,14 @@ Not-null constraints: regress_publication_user | t | f | t | t | f | f | none | f | (1 row) +-- fail - can't drop an EXCEPT TABLE from schema publication +ALTER PUBLICATION testpub_forschema DROP EXCEPT TABLE (pub_test.testpub_nopk); +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 --------------------------------------------- @@ -275,12 +283,18 @@ ALTER PUBLICATION testpub_foralltables_excepttable SET EXCEPT TABLE (testpub_tbl 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 | Description +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- + 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; @@ -388,7 +402,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. +HINT: Modify the publication's EXCEPT clause using ALTER PUBLICATION ... SET/DROP EXCEPT TABLE. RESET client_min_messages; DROP TABLE testpub_root, testpub_part1, tab_main; DROP PUBLICATION testpub8; diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index ce0ece4abc0..42703ddac5d 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -74,6 +74,8 @@ ALTER PUBLICATION testpub_fortable DROP TABLES IN SCHEMA pub_test; ALTER PUBLICATION testpub_fortable SET TABLES IN SCHEMA pub_test; \dRp+ testpub_fortable +-- fail - can't drop an EXCEPT TABLE from 'FOR TABLE' publication +ALTER PUBLICATION testpub_fortable DROP EXCEPT TABLE (testpub_tbl1); -- fail - can't set an EXCEPT TABLE to 'FOR TABLE' publication ALTER PUBLICATION testpub_fortable SET EXCEPT TABLE (testpub_tbl1); @@ -108,6 +110,8 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall \d+ testpub_tbl2 \dRp+ testpub_foralltables +-- fail - can't drop an EXCEPT TABLE from schema publication +ALTER PUBLICATION testpub_forschema DROP EXCEPT TABLE (pub_test.testpub_nopk); -- fail - can't set an EXCEPT TABLE to schema publication ALTER PUBLICATION testpub_forschema SET EXCEPT TABLE (pub_test.testpub_nopk); @@ -132,8 +136,9 @@ CREATE PUBLICATION test_pub_forexcepttable FOR EXCEPT TABLE (testpub_tbl1); 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_except.pl b/src/test/subscription/t/037_except.pl index 647ae8433ad..45f3707d69e 100644 --- a/src/test/subscription/t/037_except.pl +++ b/src/test/subscription/t/037_except.pl @@ -186,6 +186,27 @@ $result = is($result, qq(20), 'check that the data is copied as the tab1 is removed from EXCEPT TABLE clause'); +# Remove tab2 from the publication's EXCEPT list so that it becomes part +# of the ALL TABLES publication. +$node_publisher->safe_psql('postgres', + "ALTER PUBLICATION tab_pub DROP EXCEPT TABLE (tab2)"); + +# Refresh the subscription so the subscriber picks up the updated +# publication definition and initiates table synchronization. +$node_subscriber->safe_psql('postgres', + "ALTER SUBSCRIPTION tab_sub REFRESH PUBLICATION"); + +# Wait for initial table sync to finish +$node_subscriber->wait_for_subscription_sync($node_publisher, + 'tab_sub'); + +# Verify that table synchronization occurs once tab2 is removed from the +# EXCEPT TABLE clause via SET EXCEPT TABLE. +$result = + $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM tab2"); +is($result, qq(10), + 'check that the data is copied as the tab1 is removed from EXCEPT TABLE clause'); + # cleanup $node_subscriber->safe_psql( 'postgres', qq( -- 2.43.0