diff options
author | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2020-01-02 17:04:24 -0300 |
---|---|---|
committer | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2020-01-02 17:04:24 -0300 |
commit | 1fa846f1c9afe6bb185d4bb60bed8102a8eacb8f (patch) | |
tree | 7dcfe215779d995aed3ead99af0b6d0b01cf8e5a /src/backend/commands/tablecmds.c | |
parent | 7c015045b9141cc30272930ea88cfa5df47240b7 (diff) | |
download | postgresql-1fa846f1c9afe6bb185d4bb60bed8102a8eacb8f.tar.gz postgresql-1fa846f1c9afe6bb185d4bb60bed8102a8eacb8f.zip |
Fix cloning of row triggers to sub-partitions
When row triggers exist in partitioned partitions that are not either
part of FKs or deferred unique constraints, they are not correctly
cloned to their partitions. That's because they are marked "internal",
and those are purposefully skipped when doing the clone triggers dance.
Fix by relaxing the condition on which internal triggers are skipped.
Amit Langote initially diagnosed the problem and proposed a fix, but I
used a different approach.
Reported-by: Petr Fedorov
Discussion: https://postgr.es/m/6b3f0646-ba8c-b3a9-c62d-1c6651a1920f@phystech.edu
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r-- | src/backend/commands/tablecmds.c | 65 |
1 files changed, 63 insertions, 2 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 01bc292ada0..1c4394abea1 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -15932,6 +15932,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 = table_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); + table_close(pg_depend, AccessShareLock); + + return found; +} + +/* * CloneRowTriggersToPartition * subroutine for ATExecAttachPartition/DefineRelation to create row * triggers on partitions @@ -15971,8 +16019,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(trigForm->oid))) continue; /* |