aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/commands/tablecmds.c65
-rw-r--r--src/test/regress/expected/triggers.out36
-rw-r--r--src/test/regress/sql/triggers.sql4
3 files changed, 90 insertions, 15 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d207bc299f9..a120a18f029 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -15243,6 +15243,54 @@ out:
}
/*
+ * isPartitionTrigger
+ * Subroutine for CloneRowTriggersToPartition: determine whether
+ * the given trigger has been cloned from another one.
+ *
+ * We use pg_depend as a proxy for this, since we don't have any direct
+ * evidence. This is an ugly hack to cope with a catalog deficiency.
+ * Keep away from children. Do not stare with naked eyes. Do not propagate.
+ */
+static bool
+isPartitionTrigger(Oid trigger_oid)
+{
+ Relation pg_depend;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+ bool found = false;
+
+ pg_depend = heap_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0], Anum_pg_depend_classid,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(TriggerRelationId));
+ ScanKeyInit(&key[1], Anum_pg_depend_objid,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(trigger_oid));
+
+ scan = systable_beginscan(pg_depend, DependDependerIndexId,
+ true, NULL, 2, key);
+ while ((tup = systable_getnext(scan)) != NULL)
+ {
+ Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (dep->refclassid == TriggerRelationId)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ systable_endscan(scan);
+ heap_close(pg_depend, AccessShareLock);
+
+ return found;
+}
+
+/*
* CloneRowTriggersToPartition
* subroutine for ATExecAttachPartition/DefineRelation to create row
* triggers on partitions
@@ -15282,8 +15330,21 @@ CloneRowTriggersToPartition(Relation parent, Relation partition)
if (!TRIGGER_FOR_ROW(trigForm->tgtype))
continue;
- /* We don't clone internal triggers, either */
- if (trigForm->tgisinternal)
+ /*
+ * Internal triggers require careful examination. Ideally, we don't
+ * clone them.
+ *
+ * However, if our parent is a partitioned relation, there might be
+ * internal triggers that need cloning. In that case, we must
+ * skip clone it if the trigger on parent depends on another trigger.
+ *
+ * Note we dare not verify that the other trigger belongs to an
+ * ancestor relation of our parent, because that creates deadlock
+ * opportunities.
+ */
+ if (trigForm->tgisinternal &&
+ (!parent->rd_rel->relispartition ||
+ !isPartitionTrigger(HeapTupleGetOid(tuple))))
continue;
/*
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index 8d8f4f2f153..cd9bdc8c4b8 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -1979,15 +1979,22 @@ create trigger trg1 after insert on trigpart for each row execute procedure trig
create table trigpart2 partition of trigpart for values from (1000) to (2000);
create table trigpart3 (like trigpart);
alter table trigpart attach partition trigpart3 for values from (2000) to (3000);
+create table trigpart4 partition of trigpart for values from (3000) to (4000) partition by range (a);
+create table trigpart41 partition of trigpart4 for values from (3000) to (3500);
+create table trigpart42 (like trigpart);
+alter table trigpart4 attach partition trigpart42 for values from (3500) to (4000);
select tgrelid::regclass, tgname, tgfoid::regproc from pg_trigger
where tgrelid::regclass::text like 'trigpart%' order by tgrelid::regclass::text;
- tgrelid | tgname | tgfoid
------------+--------+-----------------
- trigpart | trg1 | trigger_nothing
- trigpart1 | trg1 | trigger_nothing
- trigpart2 | trg1 | trigger_nothing
- trigpart3 | trg1 | trigger_nothing
-(4 rows)
+ tgrelid | tgname | tgfoid
+------------+--------+-----------------
+ trigpart | trg1 | trigger_nothing
+ trigpart1 | trg1 | trigger_nothing
+ trigpart2 | trg1 | trigger_nothing
+ trigpart3 | trg1 | trigger_nothing
+ trigpart4 | trg1 | trigger_nothing
+ trigpart41 | trg1 | trigger_nothing
+ trigpart42 | trg1 | trigger_nothing
+(7 rows)
drop trigger trg1 on trigpart1; -- fail
ERROR: cannot drop trigger trg1 on table trigpart1 because trigger trg1 on table trigpart requires it
@@ -2001,12 +2008,15 @@ HINT: You can drop trigger trg1 on table trigpart instead.
drop table trigpart2; -- ok, trigger should be gone in that partition
select tgrelid::regclass, tgname, tgfoid::regproc from pg_trigger
where tgrelid::regclass::text like 'trigpart%' order by tgrelid::regclass::text;
- tgrelid | tgname | tgfoid
------------+--------+-----------------
- trigpart | trg1 | trigger_nothing
- trigpart1 | trg1 | trigger_nothing
- trigpart3 | trg1 | trigger_nothing
-(3 rows)
+ tgrelid | tgname | tgfoid
+------------+--------+-----------------
+ trigpart | trg1 | trigger_nothing
+ trigpart1 | trg1 | trigger_nothing
+ trigpart3 | trg1 | trigger_nothing
+ trigpart4 | trg1 | trigger_nothing
+ trigpart41 | trg1 | trigger_nothing
+ trigpart42 | trg1 | trigger_nothing
+(6 rows)
drop trigger trg1 on trigpart; -- ok, all gone
select tgrelid::regclass, tgname, tgfoid::regproc from pg_trigger
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index 337fbb5e4da..b0cac89b74c 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -1371,6 +1371,10 @@ create trigger trg1 after insert on trigpart for each row execute procedure trig
create table trigpart2 partition of trigpart for values from (1000) to (2000);
create table trigpart3 (like trigpart);
alter table trigpart attach partition trigpart3 for values from (2000) to (3000);
+create table trigpart4 partition of trigpart for values from (3000) to (4000) partition by range (a);
+create table trigpart41 partition of trigpart4 for values from (3000) to (3500);
+create table trigpart42 (like trigpart);
+alter table trigpart4 attach partition trigpart42 for values from (3500) to (4000);
select tgrelid::regclass, tgname, tgfoid::regproc from pg_trigger
where tgrelid::regclass::text like 'trigpart%' order by tgrelid::regclass::text;
drop trigger trg1 on trigpart1; -- fail