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 :
- Définir l'exception - Section DECLARE
- Lever l'exception au bon moment - Section BEGIN (dans le traitement)
- 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