PostgreSQL 触发器阻止另一个触发器工作

PostgreSQL trigger prevents another one to work

假设我们有:

create table T
(
    id bigserial primary key
  , a  int
  , b  int
  , c  int
);

我希望 c 始终等于 a+b,并且我希望防止手动更改 c。所以我创建了两个触发器:

-- 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();

如果我这样做:

update T set c = 247;

我看到错误消息 "Can't change c manually" 并且该列没有更改。太好了。

但是,如果我这样做:

insert into T (a, b) values (4, 3);

或:

update T set a = 3 where id = 2 -- suppose id = 2 exists

然后我得到了和以前一样的错误信息。显然,来自 change 触发器的 update 命令触发 no_changes 触发器阻止 c 列中的更新。

有什么想法吗?谢谢!

整个逻辑可以放在一个函数中:

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();

但如果你更喜欢拆分逻辑,那么
1) 在 before 事件
上创建两个触发器 2)"If multiple triggers of the same kind are defined for the same event, they will be fired in alphabetical order by name."所以name触发得到想要的顺序,例如trg1_check来防止ctrg2_change的变化来计算c值。