PostgreSQL trigger prevents another from running
Let's pretend that:
create table T
(
id bigserial primary key
, a int
, b int
, c int
);
I want to c
always equal a+b
and I want to prevent manual changes c
. Therefore, I create two triggers:
-- Prevents manual changes of c:
create or replace function no_changes() returns trigger as $$
begin
if old.c is distinct from new.c then
raise exception 'Can''t change c manually.';
end if;
return new;
end;
$$ language plpgsql;
create trigger no_changes
before update of c -- 'before' so prevents changes in 'c'
on T
for each row
execute procedure no_changes();
-- Do c = a + b:
create or replace function change() returns trigger as $$
begin
update T
set c = a + b
where id = new.id;
return new;
end;
$$ language plpgsql;
create trigger change
after insert or update of a, b -- 'after' so I'm sure the row is inserted/updated
on T
for each row
execute procedure change();
If I do this:
update T set c = 247;
I see the error "Cannot manually change c" and the column does not change. Fine.
However, if I do:
insert into T (a, b) values (4, 3);
Or:
update T set a = 3 where id = 2 -- suppose id = 2 exists
Then I get the same error message as before. Obviously, the command update
from change
invokes the trigger no_changes
, preventing the column from being updated c
.
Any ideas? Thank!
source to share
All logic can be put into one function:
create or replace function change() returns trigger as $$
begin
if old.c is distinct from new.c then
raise exception 'Can''t change c manually.';
end if;
new.c := new.a + new.b;
return new;
end;
$$ language plpgsql;
create trigger change
before insert or update on T
for each row
execute procedure change();
But if you prefer to separate the logic, then
1) Create both triggers on the event before
2) "If multiple triggers of the same type are defined for the same event, they will be fired in alphabetical order by name." So the name triggers to get the desired order, for example trg1_check
, to prevent changes c
and trg2_change
to calculate the value c
.
source to share