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

3
假设我们有:
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;

我看到了错误信息 "无法手动更改 c",而且列没有发生变化。很好。
但是,如果我执行以下操作:
insert into T (a, b) values (4, 3);

或者:

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

然后我收到与之前相同的错误消息。显然,“更改”触发器中的“更新”命令会触发“无更改”触发器,从而阻止对“c”列的更新。
有任何想法吗?谢谢!
1个回答

4
整个逻辑可以放在一个函数中:
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)“如果为同一事件定义了多个相同类型的触发器,则它们将按名称的字母顺序触发。” 因此,请为触发器命名以获得所需的顺序,例如使用trg1_check来防止更改ctrg2_change来计算c值。


谢谢你,@Abelisto。这两个触发器都应该是“before”触发器吗?我不能使用“after”触发器吗? - Ricardo Pérez
1
@RicardoPérezLópez,您可以。但是您无法在after触发器中使用“NEW”变量更改插入/更新的行(这是您代码的主要问题,“update”会引发“before update”触发器)。 - Abelisto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接