确定Postgresql中的触发源

Cia*_*cia 9 sql postgresql triggers postgresql-9.4

你认为可以确定PostgreSQL中触发器执行的来源吗?我们假设我有两个表如下:

CREATE TABLE tbl1 (id bigserial NOT NULL PRIMARY KEY,
    name text NOT NULL);
CREATE TABLE tbl2 (id bigserial NOT NULL PRIMARY KEY,
    owner bigint NOT NULL REFERENCES tbl1(id) ON DELETE CASCADE,
    prop text NOT NULL);
Run Code Online (Sandbox Code Playgroud)

其中tbl2使用"ON DELETE CASCADE"引用tbl1.

此外,让我们在tbl2上定义一个触发器,它在删除ROW后执行:

CREATE FUNCTION test_fn() RETURNS trigger AS $$
BEGIN
    RAISE NOTICE 'test_fn()';
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER test_delete AFTER DELETE ON tbl2
    FOR EACH ROW EXECUTE PROCEDURE test_fn();
Run Code Online (Sandbox Code Playgroud)

在删除tbl2中的ROW之后始终执行触发器,如果​​直接删除行或通过级联删除行,则独立执行.例如,下面的两个语句最终触发了触发器:

DELETE FROM tbl1 WHERE id = 1;
DELETE FROM tbl2 WHERE id = 1;
Run Code Online (Sandbox Code Playgroud)

在test_fn()里面,有可能区分这两种情况吗?即弄清楚为什么行被删除?我试图确定使用堆栈的原因(即使用GET DIAGNOSTICS stack = PG_CONTEXT),但没有任何结果.

有人可以帮我吗?非常感谢提前

kli*_*lin 2

操作的上下文(即级联删除还是简单删除)应该存储在某个地方。您可以使用自定义参数来实现此目的。tbl1当执行级联删除(从表)时,触发器按以下顺序触发:

trigger before delete on tbl1
trigger before delete on tbl2
trigger after delete on tbl1
trigger after delete on tbl2
Run Code Online (Sandbox Code Playgroud)

因此,您需要 table 上的两个触发器(删除之前和删除之后)tbl1和 table 上的删除之前的触发器tbl2

在 上创建两个触发器tbl1on在触发前函数和off触发后函数中设置自定义参数:

create or replace function tbl1_trigger_before_delete()
returns trigger language plpgsql as $$
begin
    set tbl1.cascade to on;
    return old;
end $$;

create or replace function tbl1_trigger_after_delete()
returns trigger language plpgsql as $$
begin
    set tbl1.cascade to off;
    return null;
end $$;

create trigger tbl1_trigger_before_delete
before delete on tbl1
for each row execute procedure tbl1_trigger_before_delete();

create trigger tbl1_trigger_after_delete
after delete on tbl1
for each row execute procedure tbl1_trigger_after_delete();
Run Code Online (Sandbox Code Playgroud)

tbl2触发功能中检查参数的当前值。如果参数尚未设置,则需要异常块:

create or replace function tbl2_trigger_before_delete()
returns trigger language plpgsql as $$
begin
    begin
        if current_setting('tbl1.cascade') = 'on' then
            raise notice 'cascaded';
        else
            raise exception '';
        end if;
    exception when others then
        raise notice 'not cascaded';
    end;
    return old;
end $$;

create trigger tbl2_trigger_before_delete
before delete on tbl2
for each row execute procedure tbl2_trigger_before_delete();
Run Code Online (Sandbox Code Playgroud)

测试:

insert into tbl1 values
(1, '1'),
(2, '2');

insert into tbl2 values
(1, 1, '1'),
(2, 1, '2'),
(3, 2, '3'),
(4, 2, '4');

delete from tbl1 where id = 1;
NOTICE:  cascaded
NOTICE:  cascaded
DELETE 1

delete from tbl2 where owner = 2;
NOTICE:  not cascaded
NOTICE:  not cascaded
DELETE 2    
Run Code Online (Sandbox Code Playgroud)

替代解决方案。

当在级联删除上下文中执行表上的删除之前的触发器时tbl2,诊断值 PG_EXCEPTION_CONTEXT 将设置为某个字符串,并且当删除不是级联时它为空:

create or replace function tbl2_trigger_before_delete()
returns trigger language plpgsql as $$
declare
    context text;
begin
    begin
        raise exception '';
    exception when others then
        GET STACKED DIAGNOSTICS context := PG_EXCEPTION_CONTEXT;
    end;
    if context = '' then
        raise notice 'not cascaded';
    else
        raise notice 'cascaded';
    end if;
    return old;
end $$;

create trigger tbl2_trigger_before_delete
before delete on tbl2
for each row execute procedure tbl2_trigger_before_delete();
Run Code Online (Sandbox Code Playgroud)

这个解决方案可能是有问题的,因为它仅来自测试,这种行为没有在任何地方记录。