2015-12-17 70 views
9

您認爲可以確定PostgreSQL中觸發器執行的來源嗎?假設我有兩個表格,如下所示:確定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觸發:

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; 

的test_fn()內,是可以區分這兩種情況?即找出爲什麼行被刪除?我試圖確定使用堆棧的原因(即使用GET DIAGNOSTICS stack = PG_CONTEXT),但沒有出現。

有人可以幫我嗎? 非常感謝

回答

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上創建兩個觸發器。在觸發前的功能自定義參數設置爲onoff在後觸發功能:

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

嗨克林,第一解決方案是「有效的」,但並不完全符合我的要求。也許我還不夠清楚,但在我寫的賞金評論中,我知道如何解決它(使用多個觸發器),但我想知道Postgresql是否提供這種信息(引用),而不是我可以將信息存儲在某處供以後使用。對於您的信息,第二次嘗試(使用'PG_EXCEPTION_CONTEXT')在運行Postgres 9.4的MacOSX上不起作用,也許這是您注意到的未記錄行爲的副作用。 – Ciaccia

+0

我明白了。其實,我錯過了你的額外評論。至於第二種選擇,我也在Postgres 9.4中測試了這個解決方案。你確定你在**刪除之前測試了一個觸發器**嗎? (刪除後的觸發不能以這種方式工作)。 – klin

+0

您好@klin,我的壞,我在**之前使用了** **而不是** **之前。我可以在不使用異常技巧的情況下區分這兩種情況,只需使用'GET DIAGNOSTICS stack = PG_CONTEXT'。正如我在我原來的問題中寫的,我最初嘗試這種方式,但沒有出現(實際上是因爲之後/之前觸發)。這是非常糟糕的,因爲在PG_CONTEXT只在**觸發器之前工作的任何地方都沒有記錄。此外,這並不完全幫助我,因爲在**之前**觸發器,我仍然不確定該行是否真的被刪除。 – Ciaccia

1

您是否嘗試過在查詢中使用EXPLAIN ANALYZE來查看PostgreSQL查詢分析器的內容。根據PostgreSQL v9。4文檔,Postgres將顯示

「EXPLAIN ANALYZE顯示的執行時間包括執行程序啓動和關閉時間以及運行觸發器的時間,但不包括解析,重寫,或計劃時間執行BEFORE觸發器所花費的時間(如果有的話)包含在相關插入,更新或刪除節點的時間中;但執行AFTER觸發器所用的時間不計在那裏,因爲AFTER觸發器在完成整個計劃,每個觸發器(BEFORE或AFTER)花費的總時間也單獨顯示,請注意,延遲約束觸發器將在事務結束之前不會執行,因此EXPLAIN ANALYSE完全不考慮。

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

如果你正在使用PostgreSQL 9.1,但是,你需要使用EXPLAIN具有詳細ANALYZE獲得相同的功能(http://www.postgresql.org/docs/9.1/static/sql-explain.html