From 0a02f5dc491d2ee1eaf0dbb7d41621d3265242b9 Mon Sep 17 00:00:00 2001 From: Shlok Kyal Date: Thu, 30 Oct 2025 10:52:56 +0530 Subject: [PATCH v28 1/4] Add RESET clause to Alter Publication which will reset the publication with default values. This patch adds a new RESET clause to ALTER PUBLICATION which will reset the publication to the default state. This includes resetting the publication parameters, setting ALL TABLES and ALL SEQUENCES flags to false and dropping the relations and schemas that are associated with the publication. Usage: ALTER PUBLICATION pub1 RESET; --- doc/src/sgml/ref/alter_publication.sgml | 37 ++++++-- src/backend/commands/publicationcmds.c | 109 ++++++++++++++++++++-- src/backend/parser/gram.y | 13 ++- src/bin/psql/tab-complete.in.c | 2 +- src/include/catalog/pg_publication.h | 10 ++ src/include/nodes/parsenodes.h | 1 + src/test/regress/expected/publication.out | 57 +++++++++++ src/test/regress/sql/publication.sql | 34 +++++++ 8 files changed, 247 insertions(+), 16 deletions(-) diff --git a/doc/src/sgml/ref/alter_publication.sgml b/doc/src/sgml/ref/alter_publication.sgml index 8dd250d2f3b..ccb276eb21d 100644 --- a/doc/src/sgml/ref/alter_publication.sgml +++ b/doc/src/sgml/ref/alter_publication.sgml @@ -27,6 +27,7 @@ ALTER PUBLICATION name DROP name SET ( publication_parameter [= value] [, ... ] ) ALTER PUBLICATION name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } ALTER PUBLICATION name RENAME TO new_name +ALTER PUBLICATION name RESET where publication_object is one of: @@ -73,18 +74,34 @@ ALTER PUBLICATION name RENAME TO - The remaining variants change the owner and the name of the publication. + The OWNER clause will change the owner of the + publication. + + + + The RENAME clause will change the name of the + publication. + + + + The RESET clause will reset the publication to the default + state. This includes resetting all publication parameters, setting the + pg_publication.puballtables + and + pg_publication.puballsequences + to false, and removing all tables and schemas that were + explicitly added to the publication. You must own the publication to use ALTER PUBLICATION. Adding a table to a publication additionally requires owning that table. - The ADD TABLES IN SCHEMA and - SET TABLES IN SCHEMA to a publication requires the - invoking user to be a superuser. - To alter the owner, you must be able to SET ROLE to the - new owning role, and that role must have CREATE - privilege on the database. + The ADD TABLES IN SCHEMA, + SET TABLES IN SCHEMA to a publication and + RESET of publication requires the invoking user to be a + superuser. To alter the owner, you must be able to + SET ROLE to the new owning role, and that role must have + CREATE privilege on the database. Also, the new owner of a FOR TABLES IN SCHEMA or FOR ALL TABLES @@ -236,6 +253,12 @@ ALTER PUBLICATION sales_publication ADD TABLES IN SCHEMA marketing, sales; production_publication: ALTER PUBLICATION production_publication ADD TABLE users, departments, TABLES IN SCHEMA production; + + + + Reset the publication production_publication: + +ALTER PUBLICATION production_publication RESET; diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 1faf3a8c372..70d34c77f4d 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -90,12 +90,12 @@ parse_publication_options(ParseState *pstate, *publish_generated_columns_given = false; /* defaults */ - pubactions->pubinsert = true; - pubactions->pubupdate = true; - pubactions->pubdelete = true; - pubactions->pubtruncate = true; - *publish_via_partition_root = false; - *publish_generated_columns = PUBLISH_GENCOLS_NONE; + pubactions->pubinsert = PUB_DEFAULT_ACTION_INSERT; + pubactions->pubupdate = PUB_DEFAULT_ACTION_UPDATE; + pubactions->pubdelete = PUB_DEFAULT_ACTION_DELETE; + pubactions->pubtruncate = PUB_DEFAULT_ACTION_TRUNCATE; + *publish_via_partition_root = PUB_DEFAULT_VIA_ROOT; + *publish_generated_columns = PUB_DEFAULT_GENCOLS; /* Parse options */ foreach(lc, options) @@ -1209,6 +1209,101 @@ InvalidatePublicationRels(List *relids) CacheInvalidateRelcacheAll(); } +/* + * Reset the publication. + * + * Reset the publication parameters, setting ALL TABLES and ALL SEQUENCES flag + * to false and drop all relations and schemas that are associated with the + * publication. + */ +static void +AlterPublicationReset(ParseState *pstate, AlterPublicationStmt *stmt, + Relation rel, HeapTuple tup) +{ + Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup); + Oid pubid = pubform->oid; + List *schemaids = NIL; + List *rels = NIL; + List *relids = NIL; + bool nulls[Natts_pg_publication]; + bool replaces[Natts_pg_publication]; + Datum values[Natts_pg_publication]; + + /* RESET publication requires superuser */ + if (!superuser()) + ereport(ERROR, + errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to RESET publication")); + + memset(values, 0, sizeof(values)); + memset(nulls, false, sizeof(nulls)); + memset(replaces, false, sizeof(replaces)); + + /* Reset the publication parameters */ + values[Anum_pg_publication_pubinsert - 1] = BoolGetDatum(PUB_DEFAULT_ACTION_INSERT); + replaces[Anum_pg_publication_pubinsert - 1] = true; + + values[Anum_pg_publication_pubupdate - 1] = BoolGetDatum(PUB_DEFAULT_ACTION_UPDATE); + replaces[Anum_pg_publication_pubupdate - 1] = true; + + values[Anum_pg_publication_pubdelete - 1] = BoolGetDatum(PUB_DEFAULT_ACTION_DELETE); + replaces[Anum_pg_publication_pubdelete - 1] = true; + + values[Anum_pg_publication_pubtruncate - 1] = BoolGetDatum(PUB_DEFAULT_ACTION_TRUNCATE); + replaces[Anum_pg_publication_pubtruncate - 1] = true; + + values[Anum_pg_publication_pubviaroot - 1] = BoolGetDatum(PUB_DEFAULT_VIA_ROOT); + replaces[Anum_pg_publication_pubviaroot - 1] = true; + + values[Anum_pg_publication_pubgencols - 1] = CharGetDatum(PUB_DEFAULT_GENCOLS); + replaces[Anum_pg_publication_pubgencols - 1] = true; + + values[Anum_pg_publication_puballtables - 1] = BoolGetDatum(PUB_DEFAULT_ALL_TABLES); + replaces[Anum_pg_publication_puballtables - 1] = true; + + values[Anum_pg_publication_puballsequences - 1] = BoolGetDatum(PUB_DEFAULT_ALL_SEQUENCES); + replaces[Anum_pg_publication_puballsequences - 1] = true; + + if (pubform->puballtables) + CacheInvalidateRelcacheAll(); + + tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, + replaces); + + /* Update the catalog. */ + CatalogTupleUpdate(rel, &tup->t_self, tup); + + /* Remove the associated schemas from the publication */ + schemaids = GetPublicationSchemas(pubid); + + /* + * Schema lock is held until the publication is altered to prevent + * concurrent schema deletion. + */ + LockSchemaList(schemaids); + + /* Remove Schemas */ + PublicationDropSchemas(pubid, schemaids, true); + + /* Get all relations associated with the publication */ + relids = GetPublicationRelations(pubid, PUBLICATION_PART_ROOT); + + foreach_oid(relid, relids) + { + PublicationRelInfo *rel; + + rel = palloc(sizeof(PublicationRelInfo)); + rel->whereClause = NULL; + rel->columns = NIL; + rel->relation = table_open(relid, ShareUpdateExclusiveLock); + rels = lappend(rels, rel); + } + + /* Remove the associated relations from the publication */ + PublicationDropTables(pubid, rels, true); + CloseTableList(rels); +} + /* * Add or remove table to/from publication. */ @@ -1553,6 +1648,8 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt) if (stmt->options) AlterPublicationOptions(pstate, stmt, rel, tup); + else if (stmt->action == AP_Reset) + AlterPublicationReset(pstate, stmt, rel, tup); else { List *relations = NIL; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c3a0a354a9c..a8b9ae6182d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10904,15 +10904,17 @@ pub_all_obj_type_list: PublicationAllObjSpec * * ALTER PUBLICATION name ADD pub_obj [, ...] * - * ALTER PUBLICATION name DROP pub_obj [, ...] - * * ALTER PUBLICATION name SET pub_obj [, ...] * + * ALTER PUBLICATION name DROP pub_obj [, ...] + * * pub_obj is one of: * * TABLE table_name [, ...] * TABLES IN SCHEMA schema_name [, ...] * + * ALTER PUBLICATION name RESET + * *****************************************************************************/ AlterPublicationStmt: @@ -10954,6 +10956,13 @@ AlterPublicationStmt: n->action = AP_DropObjects; $$ = (Node *) n; } + | ALTER PUBLICATION name RESET + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->action = AP_Reset; + $$ = (Node *)n; + } ; /***************************************************************************** diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 51806597037..5d918abaa87 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2289,7 +2289,7 @@ match_previous_words(int pattern_id, /* ALTER PUBLICATION */ else if (Matches("ALTER", "PUBLICATION", MatchAny)) - COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET"); + COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "RESET", "SET"); /* ALTER PUBLICATION ADD */ else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD")) COMPLETE_WITH("TABLES IN SCHEMA", "TABLE"); diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h index 22f48bb8975..77b0a2f9eb8 100644 --- a/src/include/catalog/pg_publication.h +++ b/src/include/catalog/pg_publication.h @@ -152,6 +152,16 @@ extern Publication *GetPublication(Oid pubid); extern Publication *GetPublicationByName(const char *pubname, bool missing_ok); extern List *GetRelationPublications(Oid relid); +/* default values for flags and publication parameters */ +#define PUB_DEFAULT_ACTION_INSERT true +#define PUB_DEFAULT_ACTION_UPDATE true +#define PUB_DEFAULT_ACTION_DELETE true +#define PUB_DEFAULT_ACTION_TRUNCATE true +#define PUB_DEFAULT_VIA_ROOT false +#define PUB_DEFAULT_ALL_TABLES false +#define PUB_DEFAULT_ALL_SEQUENCES false +#define PUB_DEFAULT_GENCOLS PUBLISH_GENCOLS_NONE + /*--------- * Expected values for pub_partopt parameter of GetPublicationRelations(), * which allows callers to specify which partitions of partitioned tables diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index d14294a4ece..8cf75724a7b 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -4326,6 +4326,7 @@ typedef enum AlterPublicationAction AP_AddObjects, /* add objects to publication */ AP_DropObjects, /* remove objects from publication */ AP_SetObjects, /* set list of objects */ + AP_Reset, /* reset the publication */ } AlterPublicationAction; typedef struct AlterPublicationStmt diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index e72d1308967..4f20a911348 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -2009,6 +2009,63 @@ Tables: DROP PUBLICATION pub1; DROP PUBLICATION pub2; DROP TABLE gencols; +-- Tests for ALTER PUBLICATION ... RESET +CREATE SCHEMA pub_sch1; +CREATE TABLE pub_sch1.tbl1 (a int); +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION testpub_reset FOR ALL TABLES, ALL SEQUENCES; +RESET client_min_messages; +-- Verify that only superuser can reset a publication +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2; +SET ROLE regress_publication_user2; +ALTER PUBLICATION testpub_reset RESET; -- fail - must be superuser +ERROR: must be superuser to RESET publication +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user; +SET ROLE regress_publication_user; +-- Verify that 'ALL TABLES', 'ALL SEQUENCES' flag is reset +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+---------- + regress_publication_user | t | t | t | t | t | t | none | f +(1 row) + +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+---------- + regress_publication_user | f | f | t | t | t | t | none | f +(1 row) + +-- Verify that associated tables, schemas and the publication parameters +-- 'PUBLISH', 'PUBLISH_VIA_PARTITION_ROOT', and 'PUBLISH_GENERATED_COLUMNS' +-- are removed from the publication after RESET +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1, TABLES IN SCHEMA public; +ALTER PUBLICATION testpub_reset SET (PUBLISH_VIA_PARTITION_ROOT = 'true'); +ALTER PUBLICATION testpub_reset SET (PUBLISH = ''); +ALTER PUBLICATION testpub_reset SET (PUBLISH_GENERATED_COLUMNS = stored); +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+---------- + regress_publication_user | f | f | f | f | f | f | stored | t +Tables: + "pub_sch1.tbl1" +Tables from schemas: + "public" + +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+---------- + regress_publication_user | f | f | t | t | t | t | none | f +(1 row) + +DROP PUBLICATION testpub_reset; +DROP TABLE pub_sch1.tbl1; +DROP SCHEMA pub_sch1; RESET client_min_messages; -- Test that the INSERT ON CONFLICT command correctly checks REPLICA IDENTITY -- when the target table is published. diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 00390aecd47..7a523ad067c 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -1268,6 +1268,40 @@ DROP PUBLICATION pub1; DROP PUBLICATION pub2; DROP TABLE gencols; +-- Tests for ALTER PUBLICATION ... RESET +CREATE SCHEMA pub_sch1; +CREATE TABLE pub_sch1.tbl1 (a int); +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION testpub_reset FOR ALL TABLES, ALL SEQUENCES; +RESET client_min_messages; + +-- Verify that only superuser can reset a publication +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2; +SET ROLE regress_publication_user2; +ALTER PUBLICATION testpub_reset RESET; -- fail - must be superuser +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user; +SET ROLE regress_publication_user; + +-- Verify that 'ALL TABLES', 'ALL SEQUENCES' flag is reset +\dRp+ testpub_reset +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + +-- Verify that associated tables, schemas and the publication parameters +-- 'PUBLISH', 'PUBLISH_VIA_PARTITION_ROOT', and 'PUBLISH_GENERATED_COLUMNS' +-- are removed from the publication after RESET +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1, TABLES IN SCHEMA public; +ALTER PUBLICATION testpub_reset SET (PUBLISH_VIA_PARTITION_ROOT = 'true'); +ALTER PUBLICATION testpub_reset SET (PUBLISH = ''); +ALTER PUBLICATION testpub_reset SET (PUBLISH_GENERATED_COLUMNS = stored); +\dRp+ testpub_reset +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + +DROP PUBLICATION testpub_reset; +DROP TABLE pub_sch1.tbl1; +DROP SCHEMA pub_sch1; + RESET client_min_messages; -- Test that the INSERT ON CONFLICT command correctly checks REPLICA IDENTITY -- 2.34.1