您认为可以确定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)但没有任何结果。
有人可以帮我吗? 非常感谢提前
答案 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)