From 5889eb0b42b6f595873e4a1d3823cc49b5070623 Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Thu, 19 Jan 2023 18:13:54 +0530 Subject: [PATCH v10 1/2] 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 which includes resetting the publication parameters, setting ALL TABLES flag 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 | 35 ++++++-- src/backend/commands/publicationcmds.c | 105 ++++++++++++++++++++-- src/backend/parser/gram.y | 9 ++ src/bin/psql/tab-complete.c | 2 +- src/include/nodes/parsenodes.h | 3 +- src/test/regress/expected/publication.out | 101 +++++++++++++++++++++ src/test/regress/sql/publication.sql | 50 +++++++++++ 7 files changed, 291 insertions(+), 14 deletions(-) diff --git a/doc/src/sgml/ref/alter_publication.sgml b/doc/src/sgml/ref/alter_publication.sgml index cd20868bca..ab3cebd71c 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: @@ -67,18 +68,32 @@ 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 which includes resetting the publication parameters, setting + ALL TABLES flag to false and + dropping all relations and schemas that are associated with 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 ALL TABLES or FOR TABLES IN SCHEMA publication must be a superuser. However, a superuser can @@ -212,6 +227,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 f4ba572697..e9ee77ecd3 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -55,6 +55,14 @@ #include "utils/varlena.h" +/* CREATE PUBLICATION 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 + /* * Information used to validate the columns in the row filter expression. See * contain_invalid_rfcolumn_walker for details. @@ -93,11 +101,11 @@ parse_publication_options(ParseState *pstate, *publish_via_partition_root_given = false; /* defaults */ - pubactions->pubinsert = true; - pubactions->pubupdate = true; - pubactions->pubdelete = true; - pubactions->pubtruncate = true; - *publish_via_partition_root = false; + 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; /* Parse options */ foreach(lc, options) @@ -1079,6 +1087,91 @@ InvalidatePublicationRels(List *relids) CacheInvalidateRelcacheAll(); } +/* + * Reset the publication. + * + * Reset the publication parameters, setting ALL TABLES 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 *schemas = NIL; + List *rels = NIL; + bool nulls[Natts_pg_publication]; + bool replaces[Natts_pg_publication]; + Datum values[Natts_pg_publication]; + ObjectAddress obj; + ListCell *lc; + Oid prid; + + /* 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; + + /* Set ALL TABLES flag to false */ + if (pubform->puballtables) + { + values[Anum_pg_publication_puballtables - 1] = BoolGetDatum(PUB_DEFAULT_ALL_TABLES); + replaces[Anum_pg_publication_puballtables - 1] = true; + CacheInvalidateRelcacheAll(); + } + + tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, + replaces); + + /* Update the catalog. */ + CatalogTupleUpdate(rel, &tup->t_self, tup); + + /* Drop the schemas associated with the publication */ + schemas = GetPublicationSchemas(pubid); + PublicationDropSchemas(pubid, schemas, false); + + /* Drop the relations associated with the publication */ + rels = GetPublicationRelations(pubid, PUBLICATION_PART_ROOT); + foreach(lc, rels) + { + Oid relid = lfirst_oid(lc); + + prid = GetSysCacheOid2(PUBLICATIONRELMAP, Anum_pg_publication_rel_oid, + ObjectIdGetDatum(relid), + ObjectIdGetDatum(pubid)); + if (!OidIsValid(prid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("relation \"%s\" is not part of the publication", + get_rel_name(relid)))); + + ObjectAddressSet(obj, PublicationRelRelationId, prid); + performDeletion(&obj, DROP_CASCADE, 0); + } +} + /* * Add or remove table to/from publication. */ @@ -1401,6 +1494,8 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt) if (stmt->options) AlterPublicationOptions(pstate, stmt, rel, tup); + else if (stmt->action == AP_ResetPublication) + AlterPublicationReset(pstate, stmt, rel, tup); else { List *relations = NIL; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a0138382a1..afbe408fe1 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10501,6 +10501,8 @@ pub_obj_list: PublicationObjSpec * * ALTER PUBLICATION name SET pub_obj [, ...] * + * ALTER PUBLICATION name RESET + * * pub_obj is one of: * * TABLE table_name [, ...] @@ -10547,6 +10549,13 @@ AlterPublicationStmt: n->action = AP_DropObjects; $$ = (Node *) n; } + | ALTER PUBLICATION name RESET + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->action = AP_ResetPublication; + $$ = (Node *)n; + } ; /***************************************************************************** diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 5e1882eaea..bdfc6d8e88 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1869,7 +1869,7 @@ psql_completion(const char *text, int start, int end) /* 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/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index f39ab8586a..772fea0f82 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3789,7 +3789,8 @@ typedef enum AlterPublicationAction { AP_AddObjects, /* add objects to publication */ AP_DropObjects, /* remove objects from publication */ - AP_SetObjects /* set list of objects */ + AP_SetObjects, /* set list of objects */ + AP_ResetPublication /* reset the publication */ } AlterPublicationAction; typedef struct AlterPublicationStmt diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 427f87ea07..708cdbbd76 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -1723,6 +1723,107 @@ DROP PUBLICATION pub; DROP TABLE sch1.tbl1; DROP SCHEMA sch1 cascade; DROP SCHEMA sch2 cascade; +-- 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; +RESET client_min_messages; +-- Verify that 'ALL TABLES' flag is reset +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | t | t | t | t | t | f +(1 row) + +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | t | t | t | t | f +(1 row) + +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1; +-- Verify that tables associated with the publication are dropped after RESET +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | t | t | t | t | f +Tables: + "pub_sch1.tbl1" + +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | t | t | t | t | f +(1 row) + +ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA public; +-- Verify that schemas associated with the publication are dropped after RESET +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | t | t | t | t | f +Tables from schemas: + "public" + +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | t | t | t | t | f +(1 row) + +ALTER PUBLICATION testpub_reset SET (PUBLISH = ''); +-- Verify that 'PUBLISH' parameter is reset +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | f | f | f | f | f +(1 row) + +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | t | t | t | t | f +(1 row) + +ALTER PUBLICATION testpub_reset SET (PUBLISH_VIA_PARTITION_ROOT = 'true'); +-- Verify that 'PUBLISH_VIA_PARTITION_ROOT' parameter is reset +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | t | t | t | t | t +(1 row) + +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + Publication testpub_reset + Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root +--------------------------+------------+---------+---------+---------+-----------+---------- + regress_publication_user | f | t | t | t | t | f +(1 row) + +-- 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 +SET ROLE regress_publication_user; +DROP PUBLICATION testpub_reset; +DROP TABLE pub_sch1.tbl1; +DROP SCHEMA pub_sch1; RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; DROP ROLE regress_publication_user_dummy; diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index a47c5939d5..ec710ff33d 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -1093,6 +1093,56 @@ DROP TABLE sch1.tbl1; DROP SCHEMA sch1 cascade; DROP SCHEMA sch2 cascade; +-- 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; +RESET client_min_messages; + +-- Verify that 'ALL TABLES' flag is reset +\dRp+ testpub_reset +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1; + +-- Verify that tables associated with the publication are dropped after RESET +\dRp+ testpub_reset +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + +ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA public; + +-- Verify that schemas associated with the publication are dropped after RESET +\dRp+ testpub_reset +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + +ALTER PUBLICATION testpub_reset SET (PUBLISH = ''); + +-- Verify that 'PUBLISH' parameter is reset +\dRp+ testpub_reset +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + +ALTER PUBLICATION testpub_reset SET (PUBLISH_VIA_PARTITION_ROOT = 'true'); + +-- Verify that 'PUBLISH_VIA_PARTITION_ROOT' parameter is reset +\dRp+ testpub_reset +ALTER PUBLICATION testpub_reset RESET; +\dRp+ testpub_reset + +-- 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 +SET ROLE regress_publication_user; + +DROP PUBLICATION testpub_reset; +DROP TABLE pub_sch1.tbl1; +DROP SCHEMA pub_sch1; + RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; DROP ROLE regress_publication_user_dummy; -- 2.34.1