postgres - запретить самому правилу / триггеру вызывать себя - бесконечный цикл - PullRequest
3 голосов
/ 24 июля 2011

Скажите, у меня есть следующая таблица:

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

При выполнении UPDATE для A я не хочу обновлять только целевую строку, но я также хочу применить обновления к другой строке в 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;

Ответы [ 2 ]

3 голосов
/ 16 июня 2016

Вы можете использовать функцию pg_trigger_depth, чтобы различать инициируемую пользователем UPDATE и инициируемую триггером UPDATE. Вы даже можете поместить его в предложение WHEN вашего триггерного объявления. Вот подробности из другого вопроса .

3 голосов
/ 24 июля 2011

Чтобы избежать бесконечных циклов в триггерах, необходимо добавить дополнительное условие where, чтобы избежать повторного воздействия на строку несколько раз:

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

То же самое нельзя сделать, чтобы избежать рекурсии в правилах, потому что новые запросы добавляются, когда исходный (и новые) обрабатываются, без учета отдельных предложений 'where.

...