From b50f4b545d84363ca8645a1c15d9816ebec9a045 Mon Sep 17 00:00:00 2001 From: Shlok Kyal Date: Tue, 18 Nov 2025 20:13:06 +0530 Subject: [PATCH v28 2/4] Support ADD ALL TABLES in ALTER PUBLICATION This patch adds support for using ADD ALL TABLES in ALTER PUBLICATION, allowing an existing publication to be changed into an ALL TABLES publication. This command is permitted only when the publication is in its default state, meaning it has no tables or schemas added, its ALL TABLES and ALL SEQUENCES flags are not set, and publication options such as publish_via_root_partition, publish_generated_columns, and publish are at their default values. Usage: ALTER PUBLICATION pub1 ADD ALL TABLES --- doc/src/sgml/logical-replication.sgml | 10 ++- doc/src/sgml/ref/alter_publication.sgml | 4 +- src/backend/commands/publicationcmds.c | 89 +++++++++++++++++++++++ src/backend/parser/gram.y | 10 +++ src/bin/psql/tab-complete.in.c | 2 +- src/include/nodes/parsenodes.h | 1 + src/test/regress/expected/publication.out | 68 +++++++++++++++++ src/test/regress/sql/publication.sql | 47 ++++++++++++ 8 files changed, 225 insertions(+), 6 deletions(-) diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml index aa013f348d4..c420469feaa 100644 --- a/doc/src/sgml/logical-replication.sgml +++ b/doc/src/sgml/logical-replication.sgml @@ -2550,10 +2550,12 @@ CONTEXT: processing remote data for replication origin "pg_16395" during "INSER - To add tables to a publication, the user must have ownership rights on the - table. To add all tables in schema to a publication, the user must be a - superuser. To create a publication that publishes all tables, all tables in - schema, or all sequences automatically, the user must be a superuser. + To create a publication using FOR ALL TABLES, + FOR ALL SEQUENCES or + FOR TABLES IN SCHEMA, the user must be a superuser. To add + ALL TABLES or TABLES IN SCHEMA to a + publication, the user must be a superuser. To add tables to a publication, + the user must have ownership rights on the table. diff --git a/doc/src/sgml/ref/alter_publication.sgml b/doc/src/sgml/ref/alter_publication.sgml index ccb276eb21d..9696b8880d7 100644 --- a/doc/src/sgml/ref/alter_publication.sgml +++ b/doc/src/sgml/ref/alter_publication.sgml @@ -22,6 +22,7 @@ PostgreSQL documentation ALTER PUBLICATION name ADD publication_object [, ...] +ALTER PUBLICATION name ADD ALL TABLES ALTER PUBLICATION name SET publication_object [, ...] ALTER PUBLICATION name DROP publication_drop_object [, ...] ALTER PUBLICATION name SET ( publication_parameter [= value] [, ... ] ) @@ -96,7 +97,8 @@ ALTER PUBLICATION name RESET 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, + The ADD ALL TABLES, + 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 diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 70d34c77f4d..a699ba9dcb0 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -1304,6 +1304,81 @@ AlterPublicationReset(ParseState *pstate, AlterPublicationStmt *stmt, CloseTableList(rels); } +/* + * Check if the publication has default values. + * + * Returns true if the publication satisfies all the following conditions: + * a) Publication is not set with "FOR ALL TABLES" or "FOR ALL SEQUENCES" + * b) Publication is having default publication parameter values + * c) Publication is not associated with schemas + * d) Publication is not associated with relations + */ +static bool +CheckPublicationDefValues(HeapTuple tup) +{ + Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup); + Oid pubid = pubform->oid; + List *pubobjs = NIL; + + if (pubform->puballtables != PUB_DEFAULT_ALL_TABLES || + pubform->puballsequences != PUB_DEFAULT_ALL_SEQUENCES) + return false; + + if (pubform->pubinsert != PUB_DEFAULT_ACTION_INSERT || + pubform->pubupdate != PUB_DEFAULT_ACTION_UPDATE || + pubform->pubdelete != PUB_DEFAULT_ACTION_DELETE || + pubform->pubtruncate != PUB_DEFAULT_ACTION_TRUNCATE || + pubform->pubviaroot != PUB_DEFAULT_VIA_ROOT || + pubform->pubgencols != PUB_DEFAULT_GENCOLS) + return false; + + pubobjs = GetPublicationSchemas(pubid); + if (list_length(pubobjs)) + return false; + + pubobjs = GetPublicationRelations(pubid, PUBLICATION_PART_ROOT); + if (list_length(pubobjs)) + return false; + + return true; +} + +/* + * Set publication to publish all tables. + */ +static void +AlterPublicationSetAllTables(Relation rel, HeapTuple tup) +{ + Form_pg_publication pubform PG_USED_FOR_ASSERTS_ONLY = (Form_pg_publication) GETSTRUCT(tup); + bool nulls[Natts_pg_publication]; + bool replaces[Natts_pg_publication]; + Datum values[Natts_pg_publication]; + +#ifdef USE_ASSERT_CHECKING + Assert(!pubform->puballtables); +#endif + + /* Add ALL TABLES to the publication requires superuser */ + if (!superuser()) + ereport(ERROR, + errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to ADD ALL TABLES to the publication")); + + memset(values, 0, sizeof(values)); + memset(nulls, false, sizeof(nulls)); + memset(replaces, false, sizeof(replaces)); + + /* Set ALL TABLES flag */ + values[Anum_pg_publication_puballtables - 1] = BoolGetDatum(true); + replaces[Anum_pg_publication_puballtables - 1] = true; + + tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, + replaces); + + /* Update the catalog. */ + CatalogTupleUpdate(rel, &tup->t_self, tup); +} + /* * Add or remove table to/from publication. */ @@ -1646,6 +1721,20 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION, stmt->pubname); + if (stmt->for_all_tables) + { + bool isdefault = CheckPublicationDefValues(tup); + + if (!isdefault) + ereport(ERROR, + errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("adding ALL TABLES requires the publication to have default publication parameter values"), + errdetail("ALL TABLES or ALL SEQUENCES flag should not be set and no tables/schemas should be associated."), + errhint("Use ALTER PUBLICATION ... RESET to reset the publication")); + + AlterPublicationSetAllTables(rel, tup); + } + if (stmt->options) AlterPublicationOptions(pstate, stmt, rel, tup); else if (stmt->action == AP_Reset) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8b9ae6182d..9d648ccb47b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10913,6 +10913,8 @@ pub_all_obj_type_list: PublicationAllObjSpec * TABLE table_name [, ...] * TABLES IN SCHEMA schema_name [, ...] * + * ALTER PUBLICATION name ADD ALL TABLES + * * ALTER PUBLICATION name RESET * *****************************************************************************/ @@ -10956,6 +10958,14 @@ AlterPublicationStmt: n->action = AP_DropObjects; $$ = (Node *) n; } + | ALTER PUBLICATION name ADD_P ALL TABLES + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->for_all_tables = true; + n->action = AP_AddObjects; + $$ = (Node *)n; + } | ALTER PUBLICATION name RESET { AlterPublicationStmt *n = makeNode(AlterPublicationStmt); diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 5d918abaa87..0e11b1ba44c 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2292,7 +2292,7 @@ match_previous_words(int pattern_id, 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"); + COMPLETE_WITH("ALL TABLES", "TABLES IN SCHEMA", "TABLE"); else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") && diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 8cf75724a7b..c22d75e80a2 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -4342,6 +4342,7 @@ typedef struct AlterPublicationStmt * objects. */ List *pubobjects; /* Optional list of publication objects */ + bool for_all_tables; /* Special publication for all tables in db */ AlterPublicationAction action; /* What action to perform with the given * objects */ } AlterPublicationStmt; diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 4f20a911348..a907dcbb8f6 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -2063,6 +2063,74 @@ ALTER PUBLICATION testpub_reset RESET; regress_publication_user | f | f | t | t | t | t | none | f (1 row) +-- Tests for ALTER PUBLICATION ... ADD ALL TABLES +-- Verify that only superuser can ADD ALL TABLES +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2; +SET ROLE regress_publication_user2; +ALTER PUBLICATION testpub_reset ADD ALL TABLES; -- fail - must be superuser +ERROR: must be superuser to ADD ALL TABLES to the publication +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user; +SET ROLE regress_publication_user; +-- Can't add ALL TABLES to 'FOR TABLE' publication +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1; +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ERROR: adding ALL TABLES requires the publication to have default publication parameter values +DETAIL: ALL TABLES or ALL SEQUENCES flag should not be set and no tables/schemas should be associated. +HINT: Use ALTER PUBLICATION ... RESET to reset the publication +ALTER PUBLICATION testpub_reset RESET; +-- Can't add ALL TABLES to 'TABLES IN SCHEMA' publication +ALTER PUBLICATION testpub_reset ADD TABLES IN SCHEMA public; +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ERROR: adding ALL TABLES requires the publication to have default publication parameter values +DETAIL: ALL TABLES or ALL SEQUENCES flag should not be set and no tables/schemas should be associated. +HINT: Use ALTER PUBLICATION ... RESET to reset the publication +ALTER PUBLICATION testpub_reset RESET; +-- Can't add ALL TABLES when the 'PUBLISH' parameter does not have default value +ALTER PUBLICATION testpub_reset SET (PUBLISH = ''); +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ERROR: adding ALL TABLES requires the publication to have default publication parameter values +DETAIL: ALL TABLES or ALL SEQUENCES flag should not be set and no tables/schemas should be associated. +HINT: Use ALTER PUBLICATION ... RESET to reset the publication +ALTER PUBLICATION testpub_reset RESET; +-- Can't add ALL TABLES when the 'PUBLISH_VIA_PARTITION_ROOT' parameter does +-- not have default value +ALTER PUBLICATION testpub_reset SET (PUBLISH_VIA_PARTITION_ROOT = 'true'); +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ERROR: adding ALL TABLES requires the publication to have default publication parameter values +DETAIL: ALL TABLES or ALL SEQUENCES flag should not be set and no tables/schemas should be associated. +HINT: Use ALTER PUBLICATION ... RESET to reset the publication +ALTER PUBLICATION testpub_reset RESET; +-- Can't add ALL TABLES when the 'PUBLISH_GENERATED_COLUMNS' parameter does +-- not have default value +ALTER PUBLICATION testpub_reset SET (PUBLISH_GENERATED_COLUMNS = stored); +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ERROR: adding ALL TABLES requires the publication to have default publication parameter values +DETAIL: ALL TABLES or ALL SEQUENCES flag should not be set and no tables/schemas should be associated. +HINT: Use ALTER PUBLICATION ... RESET to reset the publication +DROP PUBLICATION testpub_reset; +-- Can't add ALL TABLES to 'ALL SEQUENCES' publication +CREATE PUBLICATION testpub_reset FOR ALL SEQUENCES; +WARNING: "wal_level" is insufficient to publish logical changes +HINT: Set "wal_level" to "logical" before creating subscriptions. +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ERROR: adding ALL TABLES requires the publication to have default publication parameter values +DETAIL: ALL TABLES or ALL SEQUENCES flag should not be set and no tables/schemas should be associated. +HINT: Use ALTER PUBLICATION ... RESET to reset the publication +ALTER PUBLICATION testpub_reset RESET; +-- Can add ALL TABLES to publication where all publication parameters are default +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +\dRp+ testpub_reset + Publication testpub_reset + 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) + +-- Can't add ALL TABLES to 'ALL TABLES' publication +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ERROR: adding ALL TABLES requires the publication to have default publication parameter values +DETAIL: ALL TABLES or ALL SEQUENCES flag should not be set and no tables/schemas should be associated. +HINT: Use ALTER PUBLICATION ... RESET to reset the publication DROP PUBLICATION testpub_reset; DROP TABLE pub_sch1.tbl1; DROP SCHEMA pub_sch1; diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 7a523ad067c..b73801932c3 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -1298,6 +1298,53 @@ ALTER PUBLICATION testpub_reset SET (PUBLISH_GENERATED_COLUMNS = stored); ALTER PUBLICATION testpub_reset RESET; \dRp+ testpub_reset +-- Tests for ALTER PUBLICATION ... ADD ALL TABLES +-- Verify that only superuser can ADD ALL TABLES +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2; +SET ROLE regress_publication_user2; +ALTER PUBLICATION testpub_reset ADD ALL TABLES; -- fail - must be superuser +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user; +SET ROLE regress_publication_user; + +-- Can't add ALL TABLES to 'FOR TABLE' publication +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1; +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ALTER PUBLICATION testpub_reset RESET; + +-- Can't add ALL TABLES to 'TABLES IN SCHEMA' publication +ALTER PUBLICATION testpub_reset ADD TABLES IN SCHEMA public; +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ALTER PUBLICATION testpub_reset RESET; + +-- Can't add ALL TABLES when the 'PUBLISH' parameter does not have default value +ALTER PUBLICATION testpub_reset SET (PUBLISH = ''); +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ALTER PUBLICATION testpub_reset RESET; + +-- Can't add ALL TABLES when the 'PUBLISH_VIA_PARTITION_ROOT' parameter does +-- not have default value +ALTER PUBLICATION testpub_reset SET (PUBLISH_VIA_PARTITION_ROOT = 'true'); +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ALTER PUBLICATION testpub_reset RESET; + +-- Can't add ALL TABLES when the 'PUBLISH_GENERATED_COLUMNS' parameter does +-- not have default value +ALTER PUBLICATION testpub_reset SET (PUBLISH_GENERATED_COLUMNS = stored); +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +DROP PUBLICATION testpub_reset; + +-- Can't add ALL TABLES to 'ALL SEQUENCES' publication +CREATE PUBLICATION testpub_reset FOR ALL SEQUENCES; +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +ALTER PUBLICATION testpub_reset RESET; + +-- Can add ALL TABLES to publication where all publication parameters are default +ALTER PUBLICATION testpub_reset ADD ALL TABLES; +\dRp+ testpub_reset + +-- Can't add ALL TABLES to 'ALL TABLES' publication +ALTER PUBLICATION testpub_reset ADD ALL TABLES; + DROP PUBLICATION testpub_reset; DROP TABLE pub_sch1.tbl1; DROP SCHEMA pub_sch1; -- 2.34.1