diff options
author | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2020-03-18 18:58:05 -0300 |
---|---|---|
committer | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2020-03-18 18:58:05 -0300 |
commit | 487e9861d0cf83e9100ad0d0369147db3ef4ea73 (patch) | |
tree | f3cc3f4e8c89154a4d12800c1d4df86ca0161cc3 /src/backend/commands/trigger.c | |
parent | b029395f5e616e0f2b1131b2c7ecb6640f30c055 (diff) | |
download | postgresql-487e9861d0cf83e9100ad0d0369147db3ef4ea73.tar.gz postgresql-487e9861d0cf83e9100ad0d0369147db3ef4ea73.zip |
Enable BEFORE row-level triggers for partitioned tables
... with the limitation that the tuple must remain in the same
partition.
Reviewed-by: Ashutosh Bapat
Discussion: https://postgr.es/m/20200227165158.GA2071@alvherre.pgsql
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 40 |
1 files changed, 28 insertions, 12 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 513427edf14..ed551ab73aa 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -222,18 +222,6 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, if (stmt->row) { /* - * BEFORE triggers FOR EACH ROW are forbidden, because they would - * allow the user to direct the row to another partition, which - * isn't implemented in the executor. - */ - if (stmt->timing != TRIGGER_TYPE_AFTER) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a partitioned table", - RelationGetRelationName(rel)), - errdetail("Partitioned tables cannot have BEFORE / FOR EACH ROW triggers."))); - - /* * Disallow use of transition tables. * * Note that we have another restriction about transition tables @@ -1658,6 +1646,7 @@ RelationBuildTriggers(Relation relation) build->tgtype = pg_trigger->tgtype; build->tgenabled = pg_trigger->tgenabled; build->tgisinternal = pg_trigger->tgisinternal; + build->tgisclone = OidIsValid(pg_trigger->tgparentid); build->tgconstrrelid = pg_trigger->tgconstrrelid; build->tgconstrindid = pg_trigger->tgconstrindid; build->tgconstraint = pg_trigger->tgconstraint; @@ -1961,6 +1950,8 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2) return false; if (trig1->tgisinternal != trig2->tgisinternal) return false; + if (trig1->tgisclone != trig2->tgisclone) + return false; if (trig1->tgconstrrelid != trig2->tgconstrrelid) return false; if (trig1->tgconstrindid != trig2->tgconstrindid) @@ -2247,6 +2238,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, { ExecForceStoreHeapTuple(newtuple, slot, false); + /* + * After a tuple in a partition goes through a trigger, the user + * could have changed the partition key enough that the tuple + * no longer fits the partition. Verify that. + */ + if (trigger->tgisclone && + !ExecPartitionCheck(relinfo, slot, estate, false)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("moving row to another partition during a BEFORE FOR EACH ROW trigger is not supported"), + errdetail("Before executing trigger \"%s\", the row was to be in partition \"%s.%s\".", + trigger->tgname, + get_namespace_name(RelationGetNamespace(relinfo->ri_RelationDesc)), + RelationGetRelationName(relinfo->ri_RelationDesc)))); + if (should_free) heap_freetuple(oldtuple); @@ -2741,6 +2747,16 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, { ExecForceStoreHeapTuple(newtuple, newslot, false); + if (trigger->tgisclone && + !ExecPartitionCheck(relinfo, newslot, estate, false)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("moving row to another partition during a BEFORE trigger is not supported"), + errdetail("Before executing trigger \"%s\", the row was to be in partition \"%s.%s\".", + trigger->tgname, + get_namespace_name(RelationGetNamespace(relinfo->ri_RelationDesc)), + RelationGetRelationName(relinfo->ri_RelationDesc)))); + /* * If the tuple returned by the trigger / being stored, is the old * row version, and the heap tuple passed to the trigger was |