diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 240fbafade4..6fd8faf8c82 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -572,12 +572,12 @@ WITH ( MODULUS numeric_literal, REM
These forms configure the firing of trigger(s) belonging to the table.
A disabled trigger is still known to the system, but is not executed
- when its triggering event occurs. For a deferred trigger, the enable
+ when its triggering event occurs. (For a deferred trigger, the enable
status is checked when the event occurs, not when the trigger function
- is actually executed. One can disable or enable a single
+ is actually executed.) One can disable or enable a single
trigger specified by name, or all triggers on the table, or only
user triggers (this option excludes internally generated constraint
- triggers such as those that are used to implement foreign key
+ triggers, such as those that are used to implement foreign key
constraints or deferrable uniqueness and exclusion constraints).
Disabling or enabling internally generated constraint triggers
requires superuser privileges; it should be done with caution since
@@ -599,7 +599,7 @@ WITH ( MODULUS numeric_literal, REM
The effect of this mechanism is that in the default configuration,
triggers do not fire on replicas. This is useful because if a trigger
is used on the origin to propagate data between tables, then the
- replication system will also replicate the propagated data, and the
+ replication system will also replicate the propagated data; so the
trigger should not fire a second time on the replica, because that would
lead to duplication. However, if a trigger is used for another purpose
such as creating external alerts, then it might be appropriate to set it
@@ -607,6 +607,12 @@ WITH ( MODULUS numeric_literal, REM
replicas.
+
+ When this command is applied to a partitioned table, the states of
+ corresponding clone triggers in the partitions are updated too,
+ unless ONLY is specified.
+
+
This command acquires a SHARE ROW EXCLUSIVE lock.
@@ -1234,7 +1240,7 @@ WITH ( MODULUS numeric_literal, REM
Disable or enable all triggers belonging to the table.
(This requires superuser privilege if any of the triggers are
- internally generated constraint triggers such as those that are used
+ internally generated constraint triggers, such as those that are used
to implement foreign key constraints or deferrable uniqueness and
exclusion constraints.)
@@ -1246,7 +1252,7 @@ WITH ( MODULUS numeric_literal, REM
Disable or enable all triggers belonging to the table except for
- internally generated constraint triggers such as those that are used
+ internally generated constraint triggers, such as those that are used
to implement foreign key constraints or deferrable uniqueness and
exclusion constraints.
@@ -1499,9 +1505,12 @@ WITH ( MODULUS numeric_literal, REM
The actions for identity columns (ADD
GENERATED, SET etc., DROP
IDENTITY), as well as the actions
- TRIGGER, CLUSTER, OWNER,
+ CLUSTER, OWNER,
and TABLESPACE never recurse to descendant tables;
that is, they always act as though ONLY were specified.
+ Actions affecting trigger states recurse to partitions of partitioned
+ tables (unless ONLY is specified), but never to
+ traditional-inheritance descendants.
Adding a constraint recurses only for CHECK constraints
that are not marked NO INHERIT.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b59cc96719e..ce32c79ae06 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14768,8 +14768,9 @@ ATExecEnableDisableTrigger(Relation rel, const char *trigname,
char fires_when, bool skip_system, bool recurse,
LOCKMODE lockmode)
{
- EnableDisableTriggerNew(rel, trigname, fires_when, skip_system, recurse,
- lockmode);
+ EnableDisableTriggerNew2(rel, trigname, InvalidOid,
+ fires_when, skip_system, recurse,
+ lockmode);
}
/*
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 36f10824077..d9d9201ac38 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1754,7 +1754,8 @@ renametrig_partition(Relation tgrel, Oid partitionId, Oid parentTriggerOid,
* to change 'tgenabled' field for the specified trigger(s)
*
* rel: relation to process (caller must hold suitable lock on it)
- * tgname: trigger to process, or NULL to scan all triggers
+ * tgname: name of trigger to process, or NULL to scan all triggers
+ * tgparent: if not zero, process only triggers with this tgparentid
* fires_when: new value for tgenabled field. In addition to generic
* enablement/disablement, this also defines when the trigger
* should be fired in session replication roles.
@@ -1766,9 +1767,9 @@ renametrig_partition(Relation tgrel, Oid partitionId, Oid parentTriggerOid,
* system triggers
*/
void
-EnableDisableTriggerNew(Relation rel, const char *tgname,
- char fires_when, bool skip_system, bool recurse,
- LOCKMODE lockmode)
+EnableDisableTriggerNew2(Relation rel, const char *tgname, Oid tgparent,
+ char fires_when, bool skip_system, bool recurse,
+ LOCKMODE lockmode)
{
Relation tgrel;
int nkeys;
@@ -1805,6 +1806,9 @@ EnableDisableTriggerNew(Relation rel, const char *tgname,
{
Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
+ if (OidIsValid(tgparent) && tgparent != oldtrig->tgparentid)
+ continue;
+
if (oldtrig->tgisinternal)
{
/* system trigger ... ok to process? */
@@ -1855,9 +1859,10 @@ EnableDisableTriggerNew(Relation rel, const char *tgname,
Relation part;
part = relation_open(partdesc->oids[i], lockmode);
- EnableDisableTriggerNew(part, NameStr(oldtrig->tgname),
- fires_when, skip_system, recurse,
- lockmode);
+ /* Match on child triggers' tgparentid, not their name */
+ EnableDisableTriggerNew2(part, NULL, oldtrig->oid,
+ fires_when, skip_system, recurse,
+ lockmode);
table_close(part, NoLock); /* keep lock till commit */
}
}
@@ -1886,16 +1891,27 @@ EnableDisableTriggerNew(Relation rel, const char *tgname,
}
/*
- * ABI-compatible wrapper for the above. To keep as close possible to the old
- * behavior, this never recurses. Do not call this function in new code.
+ * ABI-compatible wrappers to emulate old versions of the above function.
+ * Do not call these versions in new code.
*/
+void
+EnableDisableTriggerNew(Relation rel, const char *tgname,
+ char fires_when, bool skip_system, bool recurse,
+ LOCKMODE lockmode)
+{
+ EnableDisableTriggerNew2(rel, tgname, InvalidOid,
+ fires_when, skip_system,
+ recurse, lockmode);
+}
+
void
EnableDisableTrigger(Relation rel, const char *tgname,
char fires_when, bool skip_system,
LOCKMODE lockmode)
{
- EnableDisableTriggerNew(rel, tgname, fires_when, skip_system,
- true, lockmode);
+ EnableDisableTriggerNew2(rel, tgname, InvalidOid,
+ fires_when, skip_system,
+ true, lockmode);
}
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 0377438861c..0d7558ee6ef 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -170,6 +170,9 @@ extern Oid get_trigger_oid(Oid relid, const char *name, bool missing_ok);
extern ObjectAddress renametrig(RenameStmt *stmt);
+extern void EnableDisableTriggerNew2(Relation rel, const char *tgname, Oid tgparent,
+ char fires_when, bool skip_system, bool recurse,
+ LOCKMODE lockmode);
extern void EnableDisableTriggerNew(Relation rel, const char *tgname,
char fires_when, bool skip_system, bool recurse,
LOCKMODE lockmode);
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index 6d80ab1a6d8..66d473ab031 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -2718,6 +2718,40 @@ select tgrelid::regclass, tgname, tgenabled from pg_trigger
parent | tg_stmt | A
(3 rows)
+drop table parent, child1;
+-- Check processing of foreign key triggers
+create table parent (a int primary key, f int references parent)
+ partition by list (a);
+create table child1 partition of parent for values in (1);
+select tgrelid::regclass, rtrim(tgname, '0123456789') as tgname,
+ tgfoid::regproc, tgenabled
+ from pg_trigger where tgrelid in ('parent'::regclass, 'child1'::regclass)
+ order by tgrelid::regclass::text, tgfoid;
+ tgrelid | tgname | tgfoid | tgenabled
+---------+-------------------------+------------------------+-----------
+ child1 | RI_ConstraintTrigger_c_ | "RI_FKey_check_ins" | O
+ child1 | RI_ConstraintTrigger_c_ | "RI_FKey_check_upd" | O
+ parent | RI_ConstraintTrigger_c_ | "RI_FKey_check_ins" | O
+ parent | RI_ConstraintTrigger_c_ | "RI_FKey_check_upd" | O
+ parent | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_del" | O
+ parent | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_upd" | O
+(6 rows)
+
+alter table parent disable trigger all;
+select tgrelid::regclass, rtrim(tgname, '0123456789') as tgname,
+ tgfoid::regproc, tgenabled
+ from pg_trigger where tgrelid in ('parent'::regclass, 'child1'::regclass)
+ order by tgrelid::regclass::text, tgfoid;
+ tgrelid | tgname | tgfoid | tgenabled
+---------+-------------------------+------------------------+-----------
+ child1 | RI_ConstraintTrigger_c_ | "RI_FKey_check_ins" | D
+ child1 | RI_ConstraintTrigger_c_ | "RI_FKey_check_upd" | D
+ parent | RI_ConstraintTrigger_c_ | "RI_FKey_check_ins" | D
+ parent | RI_ConstraintTrigger_c_ | "RI_FKey_check_upd" | D
+ parent | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_del" | D
+ parent | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_upd" | D
+(6 rows)
+
drop table parent, child1;
-- Verify that firing state propagates correctly on creation, too
CREATE TABLE trgfire (i int) PARTITION BY RANGE (i);
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index 4d8504fb246..4222a8500b4 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -1883,6 +1883,21 @@ select tgrelid::regclass, tgname, tgenabled from pg_trigger
order by tgrelid::regclass::text, tgname;
drop table parent, child1;
+-- Check processing of foreign key triggers
+create table parent (a int primary key, f int references parent)
+ partition by list (a);
+create table child1 partition of parent for values in (1);
+select tgrelid::regclass, rtrim(tgname, '0123456789') as tgname,
+ tgfoid::regproc, tgenabled
+ from pg_trigger where tgrelid in ('parent'::regclass, 'child1'::regclass)
+ order by tgrelid::regclass::text, tgfoid;
+alter table parent disable trigger all;
+select tgrelid::regclass, rtrim(tgname, '0123456789') as tgname,
+ tgfoid::regproc, tgenabled
+ from pg_trigger where tgrelid in ('parent'::regclass, 'child1'::regclass)
+ order by tgrelid::regclass::text, tgfoid;
+drop table parent, child1;
+
-- Verify that firing state propagates correctly on creation, too
CREATE TABLE trgfire (i int) PARTITION BY RANGE (i);
CREATE TABLE trgfire1 PARTITION OF trgfire FOR VALUES FROM (1) TO (10);