确定Postgresql中触发器的源

确定Postgresql中触发器的源,sql,postgresql,triggers,postgresql-9.4,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 te

您认为可以确定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使用“ON DELETE CASCADE”引用tbl1

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

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中的一行后执行,如果直接或通过级联删除这些行,则会独立执行。例如,下面两条语句最终都会触发触发器:

DELETE FROM tbl1 WHERE id = 1;
DELETE FROM tbl2 WHERE id = 1;
在测试_fn()中,是否可以区分这两种情况?即,找出删除该行的原因?我试图使用堆栈(即GET DIAGNOSTICS stack=PG_CONTEXT)确定原因,但没有结果

有人能帮我吗?
事先多谢

操作的上下文(即级联删除还是简单删除)应该存储在某个地方。 为此,可以使用自定义参数。 执行级联删除(从表
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();
测试:


替代解决方案。

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

从这个意义上说,这个解决方案可能有问题,因为它只由测试产生,而这种行为在任何地方都没有记录。

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

EXPLAIN ANALYZE显示的执行时间包括执行器启动和关闭时间,以及运行任何触发的触发器的时间,但不包括解析、重写或计划时间。执行触发器之前所花费的时间(如果有)包括在相关插入、更新或删除节点的时间中;但是,在触发器之后执行所花费的时间并没有计算在内,因为在整个计划完成之后触发触发器。每个触发器(之前或之后)花费的总时间也单独显示。请注意,延迟约束触发器在事务结束之前不会执行,因此解释分析根本不考虑。”

(源URL:)


但是,如果您使用的是PostgreSQL v9.1,则需要使用EXPLAIN ANALYZE with VERBOSE来获得相同的功能()

Hi klin,第一个解决方案是“工作”,但不是我所要求的。也许我不够清楚,但在我写的赏金评论中,我知道如何解决它(使用多个触发器),但我想知道Postgresql是否提供此类信息(引号),而不是我可以将信息存储在某个地方供以后使用。请参考第二次尝试(使用
PG\u EXCEPTION\u CONTEXT
)在运行Postgres 9.4的MacOSX上不起作用,可能是您注意到的未记录行为的副作用。我明白了。事实上,我错过了您的补充评论。至于第二个选项,我也在Postgres 9.4中测试了解决方案。您确定在删除之前测试了触发器吗?(删除后的触发器不起作用).Hi@klin,我的错,我使用的是后触发器而不是前触发器。我可以在没有使用异常技巧的情况下区分这两种情况,只需使用
GET DIAGNOSTICS stack=PG_CONTEXT
。正如我在最初的问题中所写的,我最初尝试了这种方法,但没有结果(实际上是因为后/前触发器)。这是非常糟糕的,因为没有任何地方记录PG_上下文仅在before触发器中起作用。此外,这对我没有完全帮助,因为在before触发器中,我仍然不确定该行是否真的会被删除。
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();