CodeGym /Corsi /SQL SELF /Gestione degli errori e delle transazioni nei trigger: EX...

Gestione degli errori e delle transazioni nei trigger: EXCEPTION, RAISE

SQL SELF
Livello 58, Lezione 3
Disponibile

A volte i trigger si comportano in modo imprevedibile, e questo può essere dovuto a:

  • Errore logico nella logica della funzione collegata al trigger.
  • Violazione dei vincoli del database (tipo violazione di unicità o incompatibilità dei tipi di dati).
  • Problemi nelle transazioni, quando il trigger causa un rollback delle modifiche a causa di un errore.
  • Ricorsione, se il trigger richiama sé stesso (spesso per sbaglio).

Per evitare questi problemi, PostgreSQL ti permette di gestire gli errori dentro i trigger e le loro funzioni. Questi strumenti includono i blocchi EXCEPTION e l'istruzione RAISE, che oggi vedremo con degli esempi.

Gestione degli errori con il blocco EXCEPTION

Il blocco EXCEPTION ti permette di intercettare gli errori ed eseguire del codice per gestirli. È simile all'uso di try-catch in linguaggi come Python o Java.

Il blocco EXCEPTION si usa nelle funzioni PL/pgSQL così:

BEGIN
    -- Codice principale della funzione
EXCEPTION
    WHEN <tipo_errore> THEN
        -- Codice per gestire l'errore
END;

Dove <tipo_errore> è l'errore specifico o il gruppo di errori che vuoi gestire (tipo unique_violation, division_by_zero ecc.).

Esempio: logging degli errori nei trigger

Immagina di avere una tabella logs dove vuoi salvare gli errori che succedono quando inserisci dati nella tabella students. Ecco un esempio:

Creiamo la tabella per i log

CREATE TABLE logs (
    id SERIAL PRIMARY KEY,
    error_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    error_message TEXT
);

Creiamo una funzione con gestione degli errori

CREATE OR REPLACE FUNCTION track_insert_errors()
RETURNS TRIGGER AS $$
BEGIN
    -- Proviamo ad eseguire il codice principale
    BEGIN
        -- Esempio di azione "sbagliata": divisione per 0
        PERFORM 1 / (NEW.some_value - NEW.some_value);
    EXCEPTION
        WHEN division_by_zero THEN
            -- Se succede un errore di divisione per 0, lo salviamo nei log
            INSERT INTO logs (error_message) VALUES ('Errore di divisione per 0 durante l\'inserimento in students');
    END;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Creiamo il trigger

CREATE TRIGGER before_insert_students
BEFORE INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION track_insert_errors();

Ora, se durante l'inserimento nella tabella students succede un errore di divisione per zero, verrà gestito e l'informazione finirà nella tabella logs.

Uso di RAISE per diagnostica e debug

L'istruzione RAISE ti permette di mostrare messaggi di warning, errori o info di debug. È uno strumento super utile quando vuoi capire come funziona (o non funziona!) il tuo trigger.

Tipi di messaggi RAISE:

  1. DEBUG — messaggio di debug.
  2. NOTICE — messaggio informativo normale.
  3. WARNING — avvertimento.
  4. EXCEPTION — messaggio di errore che ferma la funzione.

Sintassi di RAISE:

RAISE <tipo_messaggio> 'Messaggio';

Puoi anche passare valori di variabili:

RAISE NOTICE 'Valore di NEW.id = %', NEW.id;

Esempio: debug dei valori in un trigger

Supponiamo che hai un errore durante l'update della tabella students e vuoi sapere quali valori di NEW e OLD stanno causando il problema. Per questo usiamo RAISE:

CREATE OR REPLACE FUNCTION debug_student_update()
RETURNS TRIGGER AS $$
BEGIN
    RAISE NOTICE 'OLD.id = %, NEW.id = %', OLD.id, NEW.id;

    -- Esempio di condizione che causa errore:
    IF NEW.some_field IS NULL THEN
        RAISE EXCEPTION 'Il campo some_field non può essere NULL';
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER after_update_students
AFTER UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION debug_student_update();

Ora, ad ogni update, vedrai i valori di OLD e NEW, e riceverai un messaggio di errore chiaro se succede qualcosa.

Transazioni nei trigger

I trigger vengono eseguiti nel contesto di una transazione. Questo significa che se succede un errore dentro il trigger o la sua funzione, tutta la transazione viene annullata. Così il database è protetto da modifiche parziali.

Però questo comportamento a volte crea problemi:

  • Se l'errore nel trigger è dovuto a dati sbagliati, sarebbe utile annullare solo alcune azioni.
  • Devi capire che il rollback della transazione include non solo il trigger, ma tutta l'operazione che ha scatenato il trigger.

Esempio: uso delle transazioni in un trigger

Per esempio, immaginiamo di voler fare della business logic che include due operazioni: aggiornare la tabella students e scrivere un log in logs. Se una delle due fallisce, tutta la transazione viene annullata.

CREATE OR REPLACE FUNCTION transactional_student_update()
RETURNS TRIGGER AS $$
BEGIN
    -- Logging del tentativo di update
    INSERT INTO logs (error_message) VALUES ('Tentativo di aggiornamento dello studente con id ' || NEW.id);

    -- Controlliamo le condizioni di business
    IF NEW.some_value IS NULL THEN
        RAISE EXCEPTION 'Il campo some_value non può essere NULL';
    END IF;

    -- Se tutto ok, ritorniamo NEW
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER before_update_students
BEFORE UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION transactional_student_update();

Errori tipici quando lavori con i trigger e come evitarli

Errori che capitano spesso agli sviluppatori:

Trigger ricorsivi. Succede quando il trigger fa modifiche che fanno scattare di nuovo il trigger. Soluzione: usa la condizione WHEN o aggiungi un flag per evitare chiamate ripetute.

Rollback di tutta la transazione per errori. Spesso non è quello che vuoi, se il trigger non è direttamente collegato ai dati principali. Soluzione: usa bene i blocchi EXCEPTION.

Troppe info di debug. Riempi i log e rendi difficile l'analisi. Soluzione: usa RAISE solo durante sviluppo e test.

Perdita di performance. Trigger complessi possono rallentare INSERT, UPDATE o DELETE. Soluzione: tieni la logica del trigger al minimo ed evita query pesanti.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION