【问题标题】:PostgreSQL trigger prevents another one to workPostgreSQL 触发器阻止另一个触发器工作
【发布时间】:2017-06-08 06:07:10
【问题描述】:

假设我们有:

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

然后我收到与以前相同的错误消息。显然,来自change 触发器的update 命令会触发no_changes 触发器,从而阻止c 列中的更新。

有什么想法吗?谢谢!

【问题讨论】:

    标签: postgresql triggers plpgsql database-trigger


    【解决方案1】:

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

    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."所以名字触发得到想要的顺序,例如trg1_check防止ctrg2_change的变化计算c值。

    【讨论】:

    • 谢谢@Abelisto。两个触发器都应该是before 触发器吗?我不能改用after 触发器吗?
    • @RicardoPérezLópez 你可以。但是您不能在after 触发器中使用NEW 变量更改插入/更新的行(这是您的代码的主要问题,update 引发了before update 触发器)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-27
    相关资源
    最近更新 更多