【问题标题】:postgres - prevent rule/trigger from calling himself - infinite looppostgres - 防止规则/触发器调用自己 - 无限循环
【发布时间】:2011-10-11 22:59:43
【问题描述】:

假设我有下表:

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,并且在返回之前更改表 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;

【问题讨论】:

标签: postgresql triggers rules infinite-loop


【解决方案1】:

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

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

为了避免规则中的递归,不能这样做,因为当原始查询(以及新查询)被解析时,新查询被抛出,而没有考虑单个查询的 where 子句。

【讨论】:

  • 我并没有真正理解你的意思?你能详细说明一下吗?我找到了替代解决方案,请参阅我的原始问题。您对此有何看法?
  • 你做了一些与我的建议类似的事情,但反过来使用两个表而不是一个表。这也有效,但如果您在任何地方依赖 FOUND,请注意返回的行数。
  • 我想避免介绍第二个虚拟表,但我并没有真正看到它。您能否添加更多见解或示例?提前非常感谢。
  • 我认为您的另一行是修订版或备份。因此,您基本上会在后触发器的定义中使用 when 条件(例如when (old.title &lt;&gt; new.title)),然后转到:update A set title = old.title where id = some_variable and title &lt;&gt; old.title。这将避免触发调用和实际更新,除非更新时。
【解决方案2】:

您可以使用函数pg_trigger_depth 来区分用户发起的UPDATE 和触发器发起的UPDATE。您甚至可以将其放入触发器声明的WHEN 子句中。这里是details from another question

【讨论】:

    猜你喜欢
    • 2011-03-08
    • 2016-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-02
    • 2021-01-11
    • 2012-05-08
    • 1970-01-01
    相关资源
    最近更新 更多