CodeGym /Cours /SQL SELF /Gestion des erreurs et transactions dans les triggers : E...

Gestion des erreurs et transactions dans les triggers : EXCEPTION, RAISE

SQL SELF
Niveau 58, Leçon 3
Disponible

Parfois, les triggers se comportent de façon chelou, et ça peut venir de :

  • Une erreur logique dans la logique de la fonction liée au trigger.
  • Une violation des contraintes de la base de données (genre, violation d'unicité ou mauvais type de données).
  • Des soucis dans les transactions, quand le trigger provoque un rollback à cause d'une erreur.
  • De la récursivité, si le trigger s'appelle lui-même (souvent par accident).

Pour éviter ce genre de galères, PostgreSQL te permet de gérer les erreurs à l'intérieur des triggers et de leurs fonctions. Ces outils incluent les blocs EXCEPTION et l'opérateur RAISE, qu'on va décortiquer avec des exemples.

Gestion des erreurs avec le bloc EXCEPTION

Le bloc EXCEPTION te permet d'attraper les erreurs et d'exécuter du code pour les gérer. C'est un peu comme le try-catch dans des langages comme Python ou Java.

Le bloc EXCEPTION s'utilise dans les fonctions PL/pgSQL comme ça :

BEGIN
    -- Code principal de la fonction
EXCEPTION
    WHEN <type_erreur> THEN
        -- Code de gestion de l'erreur
END;

Où <type_erreur> — c'est l'erreur précise ou le groupe d'erreurs que tu veux gérer (genre unique_violation, division_by_zero, etc.).

Exemple : log des erreurs dans les triggers

Imagine qu'on a une table logs où on veut enregistrer les erreurs qui arrivent quand on insère des données dans la table students. Voilà comment faire :

On crée une table pour les logs

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

On crée une fonction avec gestion d'erreur

CREATE OR REPLACE FUNCTION track_insert_errors()
RETURNS TRIGGER AS $$
BEGIN
    -- On tente d'exécuter le code principal
    BEGIN
        -- Exemple d'action "foireuse" : division par 0
        PERFORM 1 / (NEW.some_value - NEW.some_value);
    EXCEPTION
        WHEN division_by_zero THEN
            -- Si y'a une erreur de division par zéro, on la log
            INSERT INTO logs (error_message) VALUES ('Erreur de division par zéro lors de l\'insertion dans students');
    END;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

On crée le trigger

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

Maintenant, si tu as une erreur de division par zéro en insérant dans students, elle sera gérée et l'info sera enregistrée dans la table logs.

Utilisation de RAISE pour le debug et le diagnostic

L'opérateur RAISE te permet d'afficher des messages d'avertissement, d'erreur ou d'info pour le debug. C'est super pratique quand tu veux piger comment marche (ou marche pas !) ton trigger.

Types de messages RAISE :

  1. DEBUG — message de debug.
  2. NOTICE — message d'info classique.
  3. WARNING — avertissement.
  4. EXCEPTION — message d'erreur qui stoppe la fonction.

Syntaxe de RAISE :

RAISE <type_message> 'Message';

Tu peux aussi passer des valeurs de variables :

RAISE NOTICE 'Valeur de NEW.id = %', NEW.id;

Exemple : debug des valeurs dans un trigger

Imaginons que tu as une erreur en mettant à jour la table students et tu veux savoir quelles valeurs de NEW et OLD posent problème. Utilise RAISE :

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

    -- Exemple de condition qui déclenche une erreur :
    IF NEW.some_field IS NULL THEN
        RAISE EXCEPTION 'Le champ some_field ne peut pas être 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();

Maintenant, à chaque update, tu verras les valeurs de OLD et NEW, et tu auras un message d'erreur clair si ça plante.

Transactions dans les triggers

Les triggers s'exécutent dans le contexte d'une transaction. Ça veut dire que si une erreur arrive n'importe où dans le trigger ou sa fonction, toute la transaction est rollback. Ça protège bien la base contre les changements partiels.

Mais ce comportement peut parfois être relou :

  • Si l'erreur dans le trigger vient de mauvaises données, parfois tu voudrais rollback qu'une partie des actions.
  • Faut piger que le rollback concerne non seulement le trigger, mais toute l'opé qui a déclenché ce trigger.

Exemple : utilisation des transactions dans un trigger

Pour illustrer, imagine qu'on veut faire une logique métier qui inclut deux actions : update la table students et écrire un log dans logs. Si une des deux foire, toute la transaction est rollback.

CREATE OR REPLACE FUNCTION transactional_student_update()
RETURNS TRIGGER AS $$
BEGIN
    -- Log de la tentative d'update
    INSERT INTO logs (error_message) VALUES ('Tentative de mise à jour de l\'étudiant avec id ' || NEW.id);

    -- On vérifie les conditions métier
    IF NEW.some_value IS NULL THEN
        RAISE EXCEPTION 'Le champ some_value ne peut pas être NULL';
    END IF;

    -- Si tout roule, on retourne NEW
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

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

Erreurs typiques avec les triggers et comment les éviter

Les erreurs que les devs font souvent :

Triggers récursifs. Ça arrive quand un trigger fait des modifs qui le relancent encore. Solution : utiliser une condition WHEN ou ajouter un flag pour éviter les appels en boucle.

Rollback de toute la transaction à cause d'erreurs. Souvent, c'est pas ce qu'on veut, surtout si le trigger n'est pas lié direct aux données principales. Solution : bien utiliser les blocs EXCEPTION.

Trop d'infos de debug. Ça pollue les logs et rend l'analyse galère. Solution : utilise RAISE seulement pendant le dev et les tests.

Perte de perf. Les triggers compliqués peuvent ralentir les INSERT, UPDATE ou DELETE. Solution : garde la logique du trigger au minimum et évite les requêtes lourdes.

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