2015-12-17 31 views
9

Pensi che sia possibile determinare l'origine di un'esecuzione di trigger in PostgreSQL? Supponiamo che io ho due tabelle come segue:Determina la sorgente di un trigger in 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); 

Dove TBL2 riferimenti tbl1 con un "ON DELETE CASCADE".

Inoltre, definiamo un trigger su tbl2 che viene eseguito dopo una riga viene eliminata:

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

Il trigger viene sempre eseguito dopo una riga in tbl2 viene eliminato, indipendentemente se le righe sono rimossi direttamente o tramite in cascata. Per esempio entrambe le istruzioni qui sotto alla fine fuoco il grilletto:

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

All'interno della test_fn(), è possibile distinguere i due casi? Cioè capire perché la riga viene rimossa? Ho provato a determinare il motivo usando lo stack (cioè con GET DIAGNOSTICS stack = PG_CONTEXT) ma non è uscito nulla.

Qualcuno può aiutarmi qui? Grazie mille in anticipo

risposta

2

Il contesto dell'operazione (ovvero eliminazione in cascata o eliminazione semplice) deve essere memorizzato da qualche parte. È possibile utilizzare un parametro personalizzato per questo scopo. Quando la cascata eliminare (dalla tabella tbl1) viene eseguita, trigger vengono lanciati nel seguente ordine:

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

È necessario quindi due trigger (prima e dopo la cancellazione) sul tavolo tbl1 e un trigger prima di cancellare il tavolo tbl2 .

Creare due trigger su tbl1. Impostare il parametro personalizzato per on in funzione prima grilletto e al off nella funzione grilletto dopo:

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

In tbl2 funzione di trigger controllare il valore corrente del parametro. blocco eccezione è necessaria nel caso in cui il parametro non è ancora stata fissata:

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

prova:

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  

soluzione alternativa.

Quando il grilletto prima di cancellare il tavolo tbl2 viene eseguito nel contesto della cascata eliminare, il PG_EXCEPTION_CONTEXT valore diagnostico è impostato su una stringa ed è vuoto quando l'eliminazione non è collegato in cascata:

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

Questa soluzione può essere discutibile in questo senso che risulta solo dai test, questo comportamento non è documentato da nessuna parte.

+1

Hi klin, il primo la soluzione è "funzionante" ma non era esattamente quello che chiedevo. Forse non ero abbastanza chiaro, ma nel commento di generosità che ho scritto sapevo come risolverlo (usando più trigger), ma volevo sapere se Postgresql _offre questo tipo di informazioni_ (citazione), non potrei memorizzare le informazioni da qualche parte per un uso successivo. Per vostra informazione il secondo tentativo (con 'PG_EXCEPTION_CONTEXT') non funziona sul mio MacOSX con Postgres 9.4, forse è un effetto collaterale del comportamento non documentato che avete notato. – Ciaccia

+0

Capisco. In realtà, ho perso il tuo commento aggiuntivo. Per quanto riguarda la seconda opzione, ho testato anche la soluzione su Postgres 9.4. Sei sicuro di aver testato un trigger ** prima ** cancella? (Trigger dopo l'eliminazione non funziona in questo modo). – klin

+0

Ciao @klin, il mio male, stavo usando un trigger ** dopo ** invece di un ** prima **. Potrei distinguere i due casi senza usare il trucco dell'eccezione, usando semplicemente 'GET DIAGNOSTICS stack = PG_CONTEXT'. Come ho scritto nella mia domanda iniziale, inizialmente ho provato in questo modo ma non è uscito nulla (in realtà a causa del trigger precedente/precedente). Questo è pessimo perché non è documentato da nessuna parte che PG_CONTEXT funzioni solo in un ** prima ** trigger. Inoltre, questo non mi sta aiutando completamente poiché nell'attivazione ** before ** non so ancora se la riga verrà effettivamente cancellata. – Ciaccia

1

Avete provato a usare EXPLAIN ANALYZE sulla query per vedere cosa viene fuori dall'analizzatore di query PostgreSQL. Secondo PostgreSQL v9.4 documentazione, Postgres mostrerà

"Il tempo di esecuzione mostrato da EXPLAIN ANALYZE include l'ora di avvio e di spegnimento dell'executor, nonché il tempo di eseguire i trigger attivati, ma non include l'analisi, la riscrittura o tempo di pianificazione.Il tempo impiegato per l'esecuzione dei trigger BEFORE, se presente, è incluso nel tempo per il relativo nodo Insert, Update o Delete, ma il tempo trascorso nell'esecuzione dei trigger AFTER non viene conteggiato poiché i trigger AFTER vengono attivati ​​dopo il completamento dell'intero Il tempo totale trascorso in ogni trigger (sia PRIMA che DOPO) viene anche mostrato separatamente.Tieni che i trigger di vincoli posticipati non verranno eseguiti fino alla fine della transazione e quindi non vengono considerati affatto da SPIEGARE ANALIZZA. "

(URL fonte: http://www.postgresql.org/docs/9.4/static/using-explain.html)

Se stai usando PostgreSQL v9.1, tuttavia, è necessario utilizzare EXPLAIN ANALYZE con VERBOSE per ottenere la stessa funzionalità (http://www.postgresql.org/docs/9.1/static/sql-explain.html)