确定Postgresql中的触发源

时间:2015-12-17 10:50:22

标签: 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);

其中tbl2引用tbl1和“ON DELETE CASCADE”。

此外,让我们在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();

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

DELETE FROM tbl1 WHERE id = 1;
DELETE FROM tbl2 WHERE id = 1;

在test_fn()中,是否可以区分这两种情况?即找出为什么删除行?我试图确定使用堆栈的原因(即使用GET DIAGNOSTICS stack = PG_CONTEXT)但没有任何结果。

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

2 个答案:

答案 0 :(得分:2)

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

trigger before delete on tbl1
trigger before delete on tbl2
trigger after delete on tbl1
trigger after delete on tbl2

因此,您需要在表tbl1上使用两个触发器(删除之前和之后),并在表tbl2上删除之前触发。

tbl1上创建两个触发器。在前触发功能中将自定义参数设置为on,在后触发功能中设置为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();

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();

测试:

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    

替代解决方案。

当在级联删除的上下文中执行表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();

这种解决方案在这个意义上可能有问题,它只能来自测试,这种行为在任何地方都没有记录。

答案 1 :(得分:1)

您是否尝试在查询中使用EXPLAIN ANALYZE来查看PostgreSQL查询分析器的内容。根据PostgreSQL v9.4文档,Postgres将显示

“EXPLAIN ANALYZE显示的执行时间包括执行程序启动和关闭时间,以及运行任何触发的触发器的时间,但不包括解析,重写或计划时间。执行BEFORE触发器(如果有)包含在相关的Insert,Update或Delete节点的时间中;但执行AFTER触发器所花费的时间不计算在那里,因为AFTER触发器在完成整个计划后被触发。每个触发器(BEFORE或AFTER)也会单独显示。请注意,延迟约束触发器直到事务结束才会执行,因此EXPLAIN ANALYZE根本不会考虑它。“

(来源网址:http://www.postgresql.org/docs/9.4/static/using-explain.html

但是,如果您正在使用PostgreSQL v9.1,则需要使用带有VERBOSE的EXPLAIN ANALYZE来获得相同的功能(http://www.postgresql.org/docs/9.1/static/sql-explain.html