Aller au contenu

Les exceptions en PL/SQL

En PL/SQL, lorsqu'il se produit une erreur, on dit qu'une exception est levée (raised).

Une exception est une erreur qui survient lors de l’exécution d’un programme PL/SQL.

Il existe des exceptions définies par Oracle comme ZERO_DIVIDE ou VALUE_ERROR et il existe des exceptions personnalisées que le programmeur définit lui-même.

Dans un bloc PL/SQL, il existe la section EXCEPTION pour capter ces erreurs et effectuer un traitement spécifique.

L'exception permet de :

  • Gérer les erreurs proprement
  • Éviter l’arrêt brutal du programme et créer des erreurs de données
  • Donner des messages clairs à l’utilisateur

Structure d'un bloc avec Exception

La section EXCEPTION du bloc PL/SQL permet de traiter les exceptions qui peuvent survenir lors de l’exécution d’un bloc.

Pour traiter les exceptions, ces dernières doivent avoir un nom.

Ainsi, on peut traiter les exceptions par leur nom, comme suit :

BEGIN
    -- Instructions
EXCEPTION
    WHEN nom_exception THEN
        -- Traitement de l'erreur
    WHEN nomException THEN
        -- Traitement d'une autre erreur
    WHEN OTHERS THEN
        --traitement à faire pour toutes les autres exceptions non traitées individuellement
END;
/

Exemple simple :

BEGIN
    DBMS_OUTPUT.PUT_LINE(10 / 0);
EXCEPTION
    WHEN ZERO_DIVIDE THEN
        DBMS_OUTPUT.PUT_LINE('Division par zéro impossible.');
END;
/

Quelques exceptions fréquentes

Oracle fournit plusieurs exceptions déjà définies dans le système :

Exception Description
NO_DATA_FOUND Aucune donnée trouvée
TOO_MANY_ROWS Trop de lignes retournées
ZERO_DIVIDE Division par zéro
INVALID_NUMBER Conversion invalide
VALUE_ERROR Erreur de type ou taille
ROWTYPE_MISMATCH Les données insérées dans une variable ROWTYPE ne correspondent pas
INVALID_CURSOR Curseur n'existe pas ou n'est pas ouvert

EXCEPTION = ARRÊT

Il est toutefois important de savoir que lorsque l’exécution passe à la section EXCEPTION du bloc PL/SQL, on ne peut revenir dans l’exécution du bloc par la suite.

Une exception dénote qu’il y a eu un problème lors de l’exécution du bloc et permet l’arrêt du bloc de façon contrôlée plutôt que de laisser Oracle gérer la situation.

Ceci permet donc d’avoir des messages d’erreur plus détaillés, ou encore de conserver un log des événements de la base de données.

Quelques exemples d'utilisation

✔ NO_DATA_FOUND

DECLARE
    v_nom Client.nom%TYPE;
BEGIN
    SELECT nom INTO v_nom
    FROM Client
    WHERE id = 999;

EXCEPTION
    WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('Aucun client trouvé.');
END;
/

✔ TOO_MANY_ROWS

DECLARE
    v_nom Client.nom%TYPE;
BEGIN
    SELECT nom INTO v_nom
    FROM Client;

EXCEPTION
    WHEN TOO_MANY_ROWS THEN
        DBMS_OUTPUT.PUT_LINE('Plusieurs clients trouvés.');
END;
/

✔ ZERO_DIVIDE

DECLARE
    nombreParticipants NUMBER(3) := 96;
    nombreEtudiants NUMBER(3) := 0;

    pourcentage NUMBER(5,2) := 0;

BEGIN
    SELECT COUNT(noDAEtudiant) FROM ETUDIANT
    WHERE programmeEtudiant = 666; --aucun étudiant dans ce programme

    pourcentage := nombreParticipants / nombreEtudiants;

EXCEPTION
    WHEN ZERO_DIVIDE THEN
        DBMS_OUTPUT.PUT_LINE('Erreur : division par zéro ou aucun étudiant trouvé.');
END;

WHEN OTHERS

On utilise WHEN OTHERS pour gérer les erreur imprévues de façon contrôlée.

:Warning: ATTENTION :

  • Capte toutes les erreurs, donc se met à la fin des exceptions
  • Pourrait masquer un problème qui se traiterait avec une exception plus spécifique

Il est recommandé d'utiliser les codes d'exceptions prédéfinis ou encore de créer ses propres codes d'exception avant d'utiliser WHEN OTHERS qui est très vague.

BEGIN
    -- Instructions
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Erreur inattendue.'); --Message personnalisé
        DBMS_OUTPUT.PUT_LINE(SQLCODE || ' : ' || SQLERRM); --Message d'Oracle
END;
/

SQLCODE et SQLERRM

Les fonctions Oracle SQLCODE et SQLERRM permettent d'obtenir le numéro d'erreur et le message d'erreur d'Oracle pour aider à cerner le problème qui est survenu.

SQLCODE est un code numérique. Il est négatif lorsqu'une erreur survient ou donne 0 lorsque tout est beau.

SQLERRM est un message texte qui donne de l'information sur le code d'erreur.

Exemple qui sauvegarde dans une table de log :

DECLARE
    v_name VARCHAR2(20);
BEGIN
    -- Force a too_many_rows error (ORA-01422)
    SELECT first_name INTO v_name FROM employees;
EXCEPTION
    WHEN OTHERS THEN
        INSERT INTO error_log (error_code, error_message, log_date)
        VALUES (SQLCODE, SQLERRM, SYSDATE);
        COMMIT;
END;
/

Exceptions personnalisées

Le programmeur peut définir ses propres exceptions afin de traiter des situations particulières propres à la base de données.

⚠️ Une exception définie par Oracle est levée automatiquement lorsque l'erreur se produit tandis qu'une exception définie par le programmeur doit être levée par l'instruction RAISE.

Il y a donc 3 étapes dans l'utilisation d'une exception personnalisée :

  1. Définir l'exception - Section DECLARE
  2. Lever l'exception au bon moment - Section BEGIN (dans le traitement)
  3. Capter l'exception - Section EXCEPTION

Structure d'une exception personnalisée

DECLARE
    nom_exception EXCEPTION;
BEGIN
    RAISE nom_exception;
EXCEPTION
    WHEN nom_excpetion THEN
        --Traitement de l'exception
END;
/

RAISE_APPLICATION_ERROR

Les exceptions sont traitées par le bloc PL/SQL et restent au niveau du système de base de données (Oracle).

Si un bloc PL/SQl est appelé par une application, on utilise RAISE_APPLICATION_ERROR pour retourner un message d'erreur à l'application.

Bien qu'il soit possible de lever une erreur directement dans le bloc BEGIN, il est de bonne pratique de lever une exception d'abord pour ensuite lever une erreur au niveau de l'application.

Exemple :

DECLARE
    age_invalide EXCEPTION;
    age NUMBER;
BEGIN
    IF age < 1 THEN
        RAISE age_invalide;
    END IF;

EXCEPTION
    WHEN age_invalide THEN
        RAISE_APPLICATION_ERROR(-20001, 'Âge invalide.');
END;
/

Information importante

  • Utilise des codes d'erreur entre -20000 et -20999
  • Doit fournir un message d'erreur
  • Retourne le message au niveau de l'application
  • Utilisée pour capter des erreurs métier (erreur de données et non des erreurs de code, qui sont des exceptions)

Bonnes pratiques

  • Capter les exceptions spécifiques avant WHEN OTHERS
  • Donner des messages clairs
  • Utiliser des logs lorsque le code est en production
  • Utiliser les exceptions pour les erreurs de code à traiter à l'interne
  • Utiliser RAISE_APPLICATION_ERROR pour les erreurs de données à traiter par l'application