1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-28 18:48:04 +03:00

Avoid failure when altering state of partitioned foreign-key triggers.

Beginning in v15, if you apply ALTER TABLE ENABLE/DISABLE TRIGGER to
a partitioned table, it also affects the partitions' cloned versions
of the affected trigger(s).  The initial implementation of this
located the clones by name, but that fails on foreign-key triggers
which have names incorporating their own OIDs.  We can fix that, and
also make the behavior more bulletproof in the face of user-initiated
trigger renames, by identifying the cloned triggers by tgparentid.

Following the lead of earlier commits in this area, I took care not
to break ABI in the v15 branch, even though I rather doubt there
are any external callers of EnableDisableTrigger.

While here, update the documentation, which was not touched when
the semantics were changed.

Per bug #17817 from Alan Hodgson.  Back-patch to v15; older versions
do not have this behavior.

Discussion: https://postgr.es/m/17817-31dfb7c2100d9f3d@postgresql.org
This commit is contained in:
Tom Lane
2023-03-04 13:32:35 -05:00
parent 9d41ecfcd9
commit f61e60102f
6 changed files with 98 additions and 20 deletions

View File

@@ -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);
}
/*

View File

@@ -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);
}