postgres - 防止规则/触发器调用自己 - 无限循环

时间:2011-07-24 08:41:11

标签: postgresql triggers rules infinite-loop

说我有下表:

create table A
(
    identifier      integer       not null     primary key,
    title           text          not null,
    ... -- other fields
);

在A上执行UPDATE时,我一定不想只更新目标行,但我也想将更新应用到A中的另一行。我尝试编写'重写规则'或'触发前',但我总是以无限循环结束:

create function A(in A, in A) returns void as
$$
    declare
            i integer;
    begin
            -- do some logic which finds other row (hardcoded in this example)
            i = 2;

            -- update old row
            update A set title = $2.title where identifier = $1.identifier;

            -- apply updates to other row
            update A set ... where identifier = i;
    end;
$$ language plpgsql;

create rule A as on update to A do instead select A(old, new);

我测试的数据:

insert into A (identifier, title) values (1, 'old title 1');
insert into A (identifier, title) values (2, 'old title 2');

update A set title = 'new title 1' where identifier = 1;

使用“触发前”而不是“重写规则”时也会出现同样的问题。

如果需要,有没有办法绕过规则/触发器?我无法在第一行之后更改表A禁用规则A ,并且在返回之前 alter table A启用规则A ,因为表A正在使用,由我们自己。

更新

我设法通过创建一个虚拟继承表来完成此操作,在该表上完成“内部更新”,而不是直接在表上。这会绕过触发器/规则。

    drop table if exists A cascade;

    create table A
    (
        identifier      serial       not null     primary key,
        title           text         not null
    );

    create table A_
    (

    ) inherits (A);

    create or replace function A() returns trigger as
    $$
            declare
                    i integer;
            begin
                    -- create duplicate row
                    insert into A (title) values (new.title) returning identifier into i;

                    -- update new row
                    update A_ set title = new.title where identifier = i;

                    -- do not propagate update
                    return null;
            end
    $$ language plpgsql;

    create trigger A before update on A for each row execute procedure A();

    insert into A (title) values ('old title 1');
    insert into A (title) values ('old title 2');

    update A set title = 'new title 1' where identifier = 1;

    select * from A;

2 个答案:

答案 0 :(得分:3)

为了避免触发器中的无限循环,您需要添加一个额外的where子句,以避免多次重新影响一行:

update foo
set bar = 'baz'
where bar <> 'baz'

为了避免规则中的递归,不能做同样的事情,因为新的查询在原始的查询(和新的查询)被解析时被抛入,而不考虑单个查询的where子句。

答案 1 :(得分:3)

您可以使用功能pg_trigger_depth来区分用户启动的UPDATE和触发启动的UPDATE。您甚至可以将其放入触发器声明的WHEN子句中。以下是details from another question