From db98eb16d79ab1895369498b484ae288c4a72c2d Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Fri, 24 Jul 2026 00:30:07 +0900 Subject: [PATCH v2 1/3] Collect ALTER PUBLICATION mapping drops for event triggers ALTER PUBLICATION is expected to emit its own ddl_command_end entry for event triggers. Previously, however, commands that only removed publication membership entries did not do so. As a result, sql_drop reported the removed membership entries, but pg_event_trigger_ddl_commands() did not report the corresponding ALTER PUBLICATION command. This affected ALTER PUBLICATION ... DROP TABLE(S), DROP TABLES IN SCHEMA, and SET forms whose net effect was only to remove publication memberships. Fix this by collecting the publication itself as the ALTER PUBLICATION command whenever the command removes table or schema membership entries, while preserving the existing per-membership entries for additions. As a side-effect of this fix, SET commands that both add and remove memberships may now produce both a publication-level ALTER PUBLICATION entry and the existing per-membership addition entries. This is intentional, as they represent different parts of the command. Backpatch to all supported versions. --- src/backend/commands/publicationcmds.c | 56 +++++++++++++++------ src/test/regress/expected/event_trigger.out | 24 +++++++++ src/test/regress/sql/event_trigger.sql | 20 ++++++++ 3 files changed, 85 insertions(+), 15 deletions(-) diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 440adb356ad..6fa354b170c 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -66,10 +66,10 @@ static void CloseTableList(List *rels); static void LockSchemaList(List *schemalist); static void PublicationAddTables(Oid pubid, List *rels, bool if_not_exists, AlterPublicationStmt *stmt); -static void PublicationDropTables(Oid pubid, List *rels, bool missing_ok); +static bool PublicationDropTables(Oid pubid, List *rels, bool missing_ok); static void PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists, AlterPublicationStmt *stmt); -static void PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok); +static bool PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok); static char defGetGeneratedColsOption(DefElem *def); @@ -1238,7 +1238,7 @@ InvalidatePublicationRels(List *relids) /* * Add or remove table to/from publication. */ -static void +static bool AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup, List *tables, const char *queryString, bool publish_schema) @@ -1246,6 +1246,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup, List *rels = NIL; Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup); Oid pubid = pubform->oid; + bool dropped = false; /* * Nothing to do if no objects, except in SET: for that it is quite @@ -1253,7 +1254,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup, * to remove all the existing tables. */ if (!tables && stmt->action != AP_SetObjects) - return; + return false; rels = OpenTableList(tables); @@ -1269,7 +1270,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup, PublicationAddTables(pubid, rels, false, stmt); } else if (stmt->action == AP_DropObjects) - PublicationDropTables(pubid, rels, false); + dropped = PublicationDropTables(pubid, rels, false); else /* AP_SetObjects */ { List *oldrelids = NIL; @@ -1404,7 +1405,7 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup, } /* And drop them. */ - PublicationDropTables(pubid, delrels, true); + dropped = PublicationDropTables(pubid, delrels, true); /* * Don't bother calculating the difference for adding, we'll catch and @@ -1416,6 +1417,8 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup, } CloseTableList(rels); + + return dropped; } /* @@ -1423,11 +1426,12 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup, * * Add or remove schemas to/from publication. */ -static void +static bool AlterPublicationSchemas(AlterPublicationStmt *stmt, HeapTuple tup, List *schemaidlist) { Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup); + bool dropped = false; /* * Nothing to do if no objects, except in SET: for that it is quite @@ -1435,7 +1439,7 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt, * to remove all the existing schemas. */ if (!schemaidlist && stmt->action != AP_SetObjects) - return; + return false; /* * Schema lock is held until the publication is altered to prevent @@ -1478,7 +1482,7 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt, PublicationAddSchemas(pubform->oid, schemaidlist, false, stmt); } else if (stmt->action == AP_DropObjects) - PublicationDropSchemas(pubform->oid, schemaidlist, false); + dropped = PublicationDropSchemas(pubform->oid, schemaidlist, false); else /* AP_SetObjects */ { List *oldschemaids = GetPublicationSchemas(pubform->oid); @@ -1494,7 +1498,7 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt, LockSchemaList(delschemas); /* And drop them */ - PublicationDropSchemas(pubform->oid, delschemas, true); + dropped = PublicationDropSchemas(pubform->oid, delschemas, true); /* * Don't bother calculating the difference for adding, we'll catch and @@ -1502,6 +1506,8 @@ AlterPublicationSchemas(AlterPublicationStmt *stmt, */ PublicationAddSchemas(pubform->oid, schemaidlist, true, stmt); } + + return dropped; } /* @@ -1686,6 +1692,8 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt) List *exceptrelations = NIL; List *schemaidlist = NIL; Oid pubid = pubform->oid; + bool tables_dropped; + bool schemas_dropped; ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations, &exceptrelations, &schemaidlist); @@ -1712,10 +1720,20 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt) stmt->pubname)); relations = list_concat(relations, exceptrelations); - AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext, - schemaidlist != NIL); - AlterPublicationSchemas(stmt, tup, schemaidlist); + tables_dropped = + AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext, + schemaidlist != NIL); + schemas_dropped = AlterPublicationSchemas(stmt, tup, schemaidlist); AlterPublicationAllFlags(stmt, rel, tup); + + if (tables_dropped || schemas_dropped) + { + ObjectAddress obj; + + ObjectAddressSet(obj, PublicationRelationId, pubid); + EventTriggerCollectSimpleCommand(obj, InvalidObjectAddress, + (Node *) stmt); + } } /* Cleanup. */ @@ -2066,12 +2084,13 @@ PublicationAddTables(Oid pubid, List *rels, bool if_not_exists, /* * Remove listed tables from the publication. */ -static void +static bool PublicationDropTables(Oid pubid, List *rels, bool missing_ok) { ObjectAddress obj; ListCell *lc; Oid prid; + bool dropped = false; foreach(lc, rels) { @@ -2105,7 +2124,10 @@ PublicationDropTables(Oid pubid, List *rels, bool missing_ok) ObjectAddressSet(obj, PublicationRelRelationId, prid); performDeletion(&obj, DROP_CASCADE, 0); + dropped = true; } + + return dropped; } /* @@ -2137,12 +2159,13 @@ PublicationAddSchemas(Oid pubid, List *schemas, bool if_not_exists, /* * Remove listed schemas from the publication. */ -static void +static bool PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok) { ObjectAddress obj; ListCell *lc; Oid psid; + bool dropped = false; foreach(lc, schemas) { @@ -2165,7 +2188,10 @@ PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok) ObjectAddressSet(obj, PublicationNamespaceRelationId, psid); performDeletion(&obj, DROP_CASCADE, 0); + dropped = true; } + + return dropped; } /* diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out index 86ae50ce531..960f6d002e3 100644 --- a/src/test/regress/expected/event_trigger.out +++ b/src/test/regress/expected/event_trigger.out @@ -401,6 +401,14 @@ DROP ROLE regress_evt_user; DROP EVENT TRIGGER regress_event_trigger_drop_objects; DROP EVENT TRIGGER undroppable; -- Event triggers on relations. +CREATE TABLE evttrig_pub_tbl (a int); +CREATE TABLE evttrig_pub_tbl2 (a int); +CREATE TABLE evttrig_pub_tbl3 (a int); +CREATE SCHEMA evttrig_pub_schema; +CREATE SCHEMA evttrig_pub_schema2; +CREATE PUBLICATION evttrig_pub FOR TABLE evttrig_pub_tbl, + evttrig_pub_tbl2, evttrig_pub_tbl3, TABLES IN SCHEMA evttrig_pub_schema, + evttrig_pub_schema2; CREATE OR REPLACE FUNCTION event_trigger_report_dropped() RETURNS event_trigger LANGUAGE plpgsql @@ -434,6 +442,16 @@ BEGIN END; $$; CREATE EVENT TRIGGER regress_event_trigger_report_end ON ddl_command_end EXECUTE PROCEDURE event_trigger_report_end(); +ALTER PUBLICATION evttrig_pub DROP TABLE evttrig_pub_tbl; +NOTICE: NORMAL: orig=t normal=f istemp=f type=publication relation identity=public.evttrig_pub_tbl in publication evttrig_pub schema= name= addr={public,evttrig_pub_tbl} args={evttrig_pub} +NOTICE: END: command_tag=ALTER PUBLICATION type=publication identity=evttrig_pub +ALTER PUBLICATION evttrig_pub DROP TABLES IN SCHEMA evttrig_pub_schema; +NOTICE: NORMAL: orig=t normal=f istemp=f type=publication namespace identity=evttrig_pub_schema in publication evttrig_pub schema= name= addr={evttrig_pub_schema} args={evttrig_pub} +NOTICE: END: command_tag=ALTER PUBLICATION type=publication identity=evttrig_pub +ALTER PUBLICATION evttrig_pub SET TABLE evttrig_pub_tbl2; +NOTICE: NORMAL: orig=t normal=f istemp=f type=publication namespace identity=evttrig_pub_schema2 in publication evttrig_pub schema= name= addr={evttrig_pub_schema2} args={evttrig_pub} +NOTICE: NORMAL: orig=t normal=f istemp=f type=publication relation identity=public.evttrig_pub_tbl3 in publication evttrig_pub schema= name= addr={public,evttrig_pub_tbl3} args={evttrig_pub} +NOTICE: END: command_tag=ALTER PUBLICATION type=publication identity=evttrig_pub CREATE SCHEMA evttrig CREATE TABLE one (col_a SERIAL PRIMARY KEY, col_b text DEFAULT 'forty two', col_c SERIAL) CREATE INDEX one_idx ON one (col_b) @@ -590,6 +608,12 @@ NOTICE: END: command_tag=CREATE OPERATOR FAMILY type=operator family identity=p NOTICE: END: command_tag=CREATE OPERATOR CLASS type=operator class identity=public.evttrigopclass USING btree DROP EVENT TRIGGER regress_event_trigger_report_dropped; DROP EVENT TRIGGER regress_event_trigger_report_end; +DROP PUBLICATION evttrig_pub; +DROP TABLE evttrig_pub_tbl; +DROP TABLE evttrig_pub_tbl2; +DROP TABLE evttrig_pub_tbl3; +DROP SCHEMA evttrig_pub_schema; +DROP SCHEMA evttrig_pub_schema2; -- only allowed from within an event trigger function, should fail select pg_event_trigger_table_rewrite_oid(); ERROR: pg_event_trigger_table_rewrite_oid() can only be called in a table_rewrite event trigger function diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql index d0e6ba295fe..028ab53e408 100644 --- a/src/test/regress/sql/event_trigger.sql +++ b/src/test/regress/sql/event_trigger.sql @@ -301,6 +301,15 @@ DROP EVENT TRIGGER regress_event_trigger_drop_objects; DROP EVENT TRIGGER undroppable; -- Event triggers on relations. +CREATE TABLE evttrig_pub_tbl (a int); +CREATE TABLE evttrig_pub_tbl2 (a int); +CREATE TABLE evttrig_pub_tbl3 (a int); +CREATE SCHEMA evttrig_pub_schema; +CREATE SCHEMA evttrig_pub_schema2; +CREATE PUBLICATION evttrig_pub FOR TABLE evttrig_pub_tbl, + evttrig_pub_tbl2, evttrig_pub_tbl3, TABLES IN SCHEMA evttrig_pub_schema, + evttrig_pub_schema2; + CREATE OR REPLACE FUNCTION event_trigger_report_dropped() RETURNS event_trigger LANGUAGE plpgsql @@ -335,6 +344,10 @@ END; $$; CREATE EVENT TRIGGER regress_event_trigger_report_end ON ddl_command_end EXECUTE PROCEDURE event_trigger_report_end(); +ALTER PUBLICATION evttrig_pub DROP TABLE evttrig_pub_tbl; +ALTER PUBLICATION evttrig_pub DROP TABLES IN SCHEMA evttrig_pub_schema; +ALTER PUBLICATION evttrig_pub SET TABLE evttrig_pub_tbl2; + CREATE SCHEMA evttrig CREATE TABLE one (col_a SERIAL PRIMARY KEY, col_b text DEFAULT 'forty two', col_c SERIAL) CREATE INDEX one_idx ON one (col_b) @@ -417,6 +430,13 @@ CREATE OPERATOR CLASS evttrigopclass FOR TYPE int USING btree AS STORAGE int; DROP EVENT TRIGGER regress_event_trigger_report_dropped; DROP EVENT TRIGGER regress_event_trigger_report_end; +DROP PUBLICATION evttrig_pub; +DROP TABLE evttrig_pub_tbl; +DROP TABLE evttrig_pub_tbl2; +DROP TABLE evttrig_pub_tbl3; +DROP SCHEMA evttrig_pub_schema; +DROP SCHEMA evttrig_pub_schema2; + -- only allowed from within an event trigger function, should fail select pg_event_trigger_table_rewrite_oid(); -- 2.55.0