I. Public concerné▲
Testé avec Delphi 6 sous Windows 2000, Delphi 7 sous Xp pro et Oracle 9i.
version 1.0
Les fichiers sources
Je tiens à remercier Pomalaix pour ses corrections orthographiques.
II. Introduction▲
J'ai pu voir au cours de ma dernière mission que de très bons programmeurs Delphi n'utilisaient pas les exceptions Oracle pour gérer les erreurs applicatives, mais utilisaient à la place un paramètre supplémentaire dans l'appel de procédures stockées. Je peux donc supposer que cet article offrira une solution, pour les novices sous Oracle, en expliquant simplement l'utilisation des mécanismes mis à disposition par Oracle pour propager une erreur Oracle vers un client Delphi ou autre.
Dans un premier temps cet article est limité aux composants DOA pour Delphi ( ou BCB ) qui offrent, à mon avis, une des meilleures solutions sur le marché en utilisant au mieux les OCI. Ces composants proposent notamment l'utilisation d'objets Oracle (UDT) et d'Advanced queuing pour la dernière version.
Le portage de cette solution peut se faire aisément avec tous les modes d'accès SGBD de Delphi à partir du moment où vous savez récupérer le code d'erreur en provenance du serveur Oracle et non pas un code interprété par exemple avec DBexpress ( plus précisément je ne sais pas comment le récupérer ).
Le cas de figure que nous allons étudier permettra de
- centraliser, dans une table, les codes d'erreur et les messages littéraux associés ;
- tracer, également dans une table, les erreurs non traitées qui peuvent survenir pendant le cycle de développement ou en production. Offrant ainsi au support une aide précieuse ;
- déterminer simplement du côté client de quel type d'erreur il s'agit
- est-ce une erreur applicative (règle de gestion),
- une erreur Oracle, mais jugée comme une erreur applicative (par exemple insertion de doublon),
- une erreur du moteur Oracle,
- une erreur réseau,
- ou encore une erreur Delphi ?
Rien de bien complexe techniquement si ce n'est d'avoir une démarche structurée, car la centralisation des messages d'erreurs implique une méthodologie simple, mais rigoureuse. Sinon cette manière de faire devient très vite une source de problèmes au lieu d'être une solution. Vous apprendrez à vos dépens que la mise en œuvre de ce type de solution au sein d'une équipe ne supporte pas vraiment l'approximation.
III. Gestion des erreurs sous Oracle▲
Article sur les exceptions PL/SQL.
Côté serveur on utilise l'instruction PL/SQL Raise_Application_Error qui permet de déclencher une erreur dans la session Oracle de l'utilisateur puis de l'acheminer sur le poste client via NET8.
RAISE_APPLICATION_ERROR(-
20001
,'Erreur applicative n°1'
)
;
Nous utiliserons un package nommé PK_EXCEP qui regroupera les déclarations des fonctions de gestion d'erreurs utilisées dans les différents scripts PL/SQL liés à une application.
Si ce package contient des déclarations d'exceptions cela entraîne des problèmes de dépendances, i.e la livraison de ce package impliquera une invalidation et une recompilation des packages dépendants. Pour les équipes qui livrent directement dans l'environnement de production, cela peut être « gênant », je vous recommande d'éviter cette approche directe.
Dans ce cas il est du ressort du développeur de définir le numéro et le message d'erreur. On centralise ces messages d'erreurs en créant une table, de préférence dans le schéma de l'application, nommée par exemple MESSAGES_APPLICATIFS.
Cette table recensera tous les messages d'erreurs associés à un code d'erreur.
III-A. Table d'erreurs MESSAGES_APPLICATIFS▲
Cette table contient les champs suivants :
- CODE_ERREUR
contient le numéro d'erreur Oracle ( ex. : -00054) ; - NOM_ERREUR
contient le nom de l'exception attribuée, pour ce numéro d'erreur et cette application, cf. package PK_EXCEPT ; - APPLICATION
contient le nom de l'application concernée ( ex. : MonAppli) ; - MESSAGE
contient la chaîne de caractères que l'on souhaite afficher sur le client lors du déclenchement d'une erreur applicative.
On peut y insérer jusqu'à trois champs paramétrables qui sont signalés par %1, %2 et %3.
Exemple : ('%1 que vous tentez de %2 est en cours de modification.') ; - COMMENTAIRE
contient un commentaire sur le contexte d'utilisation de l'exception.
Permet de documenter l'erreur, par exemple pour commenter la déclaration d'exception dans le code PL/SQL.
Les champs CODE_ERREUR et APPLICATION constituent la clé primaire. On permet ainsi d'avoir plusieurs occurrences d'un même message d'erreur, mais pour des applications différentes.
Le champ MESSAGE est obligatoire.
Cette table doit contenir au moins la ligne suivante :
INSERT
INTO
MESSAGES_APPLICATIFS (
CODE_ERREUR, NOM_ERREUR, APPLICATION, MESSAGE,
COMMENTAIRE )
VALUES
(
-
20999
, 'E_CodeErreur_inexistant'
, 'ALL'
, 'Le numéro d''erreur (%1) ou le nom de l''application (%2)
n''est pas référencé dans la table des messages applicatifs.'
, 'Code erreur inexistant dans la table des messages d''erreurs.'
)
;
Par convention, pour cette gestion des erreurs applicatives, le code -20999 est destiné aux erreurs inconnues.
III-B. Table de trace des erreurs▲
Vous devez prévoir une purge régulière de cette table d'erreurs.
On utilisera la procédure suivante pour écrire dans cette table d'erreurs. L'utilisation de la PRAGMA AUTONOMOUS_TRANSACTION permet de réaliser une transaction autonome, c'est-à-dire une transaction indépendante de la transaction en cours qui a appelé cette procédure. On peut donc effectuer un roll back dans le script appelant sans annuler la trace de l'erreur courante.
PROCEDURE
P_TRACE_ERROR
(
P_DEBUG_ON IN
BOOLEAN
,P_NOM_PROC IN
VARCHAR2
,P_SQL_CODE IN
BINARY_INTEGER
,P_SQL_ERROR IN
VARCHAR2
)
IS
--|====================================================
--| P_TRACE_ERROR :
--|====================================================
--| Objet : Trace des erreurs survenues dans un script PL/SQL
--|
--| Entrée:
--| P_DEBUG_ON Si TRUE trace l'erreur dans la table si FALSE ne trace pas l'erreur
--| P_NOM_PROC Nom de la procédure déclenchant l'erreur
--| P_SQL_CODE Code d'erreur
--| P_SQL_ERROR Message d'erreur
--|
--| Sortie:
--|====================================================
--| 30/12/04 -> L. Dardenne : original
--|====================================================
v_UserCourant VARCHAR2
(
30
)
; -- Attention en cas d'évolution LDAP
PRAGMA
AUTONOMOUS_TRANSACTION; -- Transaction autonome indépendante de la transaction en cours qui appelle ce script.
BEGIN
IF
NOT
P_DEBUG_ON THEN
RETURN
;
END
IF
;
v_UserCurrent :=
USER
; -- Attention en cas d'évolution LDAP
INSERT
INTO
ERREURS_SCRIPT VALUES
(
P_SQL_CODE,P_NOM_PROC||
P_SQL_ERROR,v_UserCourant,SYSDATE
)
;
COMMIT
; -- On s'assure de tracer l'erreur même si la procédure appelante exécute un ROLLBACK
END
;
À noter que le composant TOracleSession de DOA propose au travers de la propriété MessageTable ce type de gestion des messages d'erreurs, mais dans ce cas elle se fait côté client.
III-C. Contenu du package PK_EXCEP▲
III-C-1. Fonction F_GET_MSG_ERR▲
Cette fonction remplace les champs paramétrables contenus dans le corps du message d'erreur extrait de la table MESSAGES_APPLICATIFS.
FUNCTION
F_GET_MSG_ERR
(
P_CODE_ERREUR IN
MESSAGES_APPLICATIFS.CODE_ERREUR%
TYPE
,P_APPLICATION IN
MESSAGES_APPLICATIFS.APPLICATION%
TYPE
,P_SPECIFICATEUR1 IN
VARCHAR2
:=
'NULL'
,P_SPECIFICATEUR2 IN
VARCHAR2
:=
'NULL'
,P_SPECIFICATEUR3 IN
VARCHAR2
:=
'NULL'
)
RETURN
VARCHAR2
IS
--|==================================================
--| F_GET_MSG_ERR :
--|==================================================
--| Objet : Renvoie un numéro d'erreur en remplaçant les spécificateurs présents dans le message associé
--| Permet d'utiliser des messages en base du type :
--| 'Erreur %1 dans le fichier %2 de la table %3'
--|
--| Entrée: P_Code_Erreur Numéro d'erreur à retrouver
--| P_Application Nom de l'application concernée
--| P_Specificateur1 Chaîne en correspondance avec %1
--| P_Specificateur2 Chaîne en correspondance avec %2
--| P_Specificateur3 Chaîne en correspondance avec %3
--|
--| Sortie: N/A
--|==================================================
--| 30/12/04 -> L.DARDENNE : original
--|==================================================
v_message MESSAGES_APPLICATIFS.MESSAGE%
TYPE
;
BEGIN
v_message:=
F_GET_ERR_APPLICATIV(
P_Code_Erreur,P_Application)
;
-- Récupére dans la table des messages d'erreurs, le message associé au numéro d'erreur
RETURN
(
F_FORMAT_MSG_ERR(
v_message,P_Specificateur1,P_Specificateur2,P_Specificateur3))
;
END
;
Les messages d'erreurs pouvant contenir des paramètres, on doit remplacer les occurrences %1,%2…
FUNCTION
F_FORMAT_MSG_ERR
(
P_CHAINE IN
OUT
VARCHAR2
,P_SPECIFICATEUR1 IN
VARCHAR2
:=
'NULL'
,P_SPECIFICATEUR2 IN
VARCHAR2
:=
'NULL'
,P_SPECIFICATEUR3 IN
VARCHAR2
:=
'NULL'
)
RETURN
VARCHAR2
IS
--|==================================================
--| F_FORMAT_MSG_ERR :
--|==================================================
--| Objet : Renvoie une chaîne de caractères en remplaçant les spécificateurs %1
--| Permet d'utiliser des chaînes de message du type :
--| 'Erreur %1 dans le fichier %2 de la table %3'
--|
--| Entrée: P_Chaine Chaîne de caractères contenant des spécificateurs
--| P_Specificateur1 Chaîne en correspondance avec %1
--| P_Specificateur2 Chaîne en correspondance avec %2
--| P_Specificateur3 Chaîne en correspondance avec %3
--|
--| Sortie: N/A
--|==================================================
--| 12/10/04 -> L.DARDENNE : original
--|==================================================
BEGIN
If
P_Specificateur1 IS
NOT
NULL
Then
P_Chaine :=
Replace
(
P_Chaine, '%1'
, P_Specificateur1 )
;
End
if
;
If
P_Specificateur2 IS
NOT
NULL
Then
P_Chaine :=
Replace
(
P_Chaine, '%2'
, P_Specificateur2 )
;
End
if
;
If
P_Specificateur3 IS
NOT
NULL
Then
P_Chaine :=
Replace
(
P_Chaine, '%3'
, P_Specificateur3 )
;
End
if
;
RETURN
(
P_Chaine)
;
END
;
Une solution basée sur une collection de variables de type AnyData peut être mise en œuvre si vous souhaitez connaître le type des paramètres afin d'effectuer quelques traitements particuliers.
III-C-2. Fonction F_GET_ERR_APPLICATIV▲
Cette fonction renvoie le message d'erreur qui correspond au couple (CODE_ERREUR, APPLICATION).
FUNCTION
F_GET_ERR_APPLICATIV
(
P_NUMERO_ERREUR IN
MESSAGES_APPLICATIFS.CODE_ERREUR%
TYPE
,P_NOM_APPLICATION IN
MESSAGES_APPLICATIFS.APPLICATION%
TYPE
)
RETURN
VARCHAR2
IS
--|==================================================
--| F_GET_ERR_APPLICATIV :
--|==================================================
--| Objet : Renvoie le message d'erreur demandé
--|
--| Entrée: P_NUMERO_ERREUR Numéro d'erreur applicative concerné ( ex. : -20123)
--| P_NOM_APPLICATION Nom de l'application concernée ( ex. : MonAppli )
--|
--| Sortie: N/A
--| Retourne : Le message d'erreur correspond au couple ( numErreur,NomAppli) ou
--| le message 'Impossible de récupérer les messages d''erreurs.' si la table est vide !
--|==================================================
--| 30/12/04 -> L.DARDENNE : original
--|==================================================
v_message MESSAGES_APPLICATIFS.MESSAGE%
TYPE
;
BEGIN
SELECT
MESSAGE INTO
v_message FROM
MESSAGES_APPLICATIFS ma
WHERE
ma.CODE_ERREUR=
P_NUMERO_ERREUR AND
ma.APPLICATION=
P_NOM_APPLICATION;
RETURN
(
v_message)
;
EXCEPTION
WHEN
NO_DATA_FOUND THEN
BEGIN
SELECT
MESSAGE INTO
v_message FROM
MESSAGES_APPLICATIFS ma
WHERE
ma.CODE_ERREUR=
c_ECodeErreur_inexistant AND
ma.APPLICATION=
'ALL'
;
-- Msg d'erreur de l'erreur !
RETURN
(
F_FORMAT_MSG_ERR(
v_message,TO_CHAR
(
P_NUMERO_ERREUR)
,P_NOM_APPLICATION))
;
EXCEPTION
WHEN
NO_DATA_FOUND THEN
v_message:=
'Impossible de récupérer les messages d''erreurs.'
;
RETURN
(
v_message)
; -- la table ne contient pas de ligne.
END
;
END
;
Ici on trace toutes les erreurs, même les erreurs applicatives que l'on a déclenchées dans le script dans la table d'erreurs.
Et surtout on redéclenche dans tous les cas l'exception afin d'avertir le client, car sans l'exécution de cette instruction RAISE l'exception ne serait pas propagée.
La recherche des erreurs non applicatives se faisant sur le test suivant :
FUNCTION
F_ERREUR_APPLICATIVE
(
P_CODE_ERREUR IN
NUMBER
)
RETURN
BOOLEAN
IS
--|====================================================
--| F_ERREUR_APPLICATIV :
--|====================================================
--| Objet : indique si le code d'erreur est un code d'erreur applicatif ou non.
--|
--| Entrée: P_CODE_ERREUR Numéro d'erreur à tester ( Cf. SQLCODE )
--|
--|====================================================
--| 11/10/2004 -> L. Dardenne : Création
--|====================================================
BEGIN
IF
(
P_CODE_ERREUR<=-
20000
)
and
(
P_CODE_ERREUR>=-
20999
)
THEN
RETURN
(
TRUE
)
;
ELSE
RETURN
(
FALSE
)
;
END
IF
;
END
;
III-C-3. Procédure P_DECLENCHE_ERREUR▲
Cette procédure encapsule la gestion des messages basés, elle appelle l'instruction Raise_Application_Error avec le message d'erreur formaté. Son utilisation allège l'écriture et la relecture du code. Mais rien ne vous empêche d'utiliser l'appel de la fonction Raise_Application_error
PROCEDURE
P_DECLENCHE_ERREUR
(
P_NUMERO_ERREUR IN
MESSAGES_APPLICATIFS.CODE_ERREUR%
TYPE
,P_NOM_APPLICATION MESSAGES_APPLICATIFS.APPLICATION%
TYPE
,P_SPECIFICATEUR1 IN
VARCHAR2
:=
'NULL'
,P_SPECIFICATEUR2 IN
VARCHAR2
:=
'NULL'
,P_SPECIFICATEUR3 IN
VARCHAR2
:=
'NULL'
)
IS
--|==================================================
--| P_DECLENCHE_ERREUR :
--|==================================================
--|==================================================
--| F_GET_MSG_ERR :
--|==================================================
--| Objet : Déclenche l'instruction Raise_Application_Error
--| Encapsule la gestion des messages basés.
--|
--| Entrée: P_Numero_Erreur Numéro d'erreur à retrouver
--| P_Nom_Application Nom de l'application concernée
--| P_Specificateur1 Chaîne en correspondance avec %1
--| P_Specificateur2 Chaîne en correspondance avec %2
--| P_Specificateur3 Chaîne en correspondance avec %3
--|
--| Sortie: N/A
--|==================================================
--| 08/10/04 -> L.DARDENNE : original
--|==================================================
BEGIN
RAISE_APPLICATION_ERROR(
P_Numero_Erreur, F_GET_MSG_ERR(
P_Numero_Erreur,
P_Nom_Application,
P_Specificateur1,
P_Specificateur2,
P_Specificateur3))
;
END
;
III-C-4. Déclaration d'une exception▲
Nous utilisons ici une convention de nommage arbitraire, libre à vous de la respecter ou pas.
Chaque exception utilisée dans un script PL/SQL doit être déclarée de la manière suivante :
III-C-5. Pour les numéros d'erreur applicative▲
Il s'agit des numéros contenus dans l'intervalle -20000..-20999.
1 - Déclarez une constante débutant par les caractères c_ de type PLS_INTEGER, ce type de donnée utilisant les registres du processeur au lieu d'une émulation pour le type number il est donc plus efficace :
c_eseq_modification_detail CONSTANT
PLS_INTEGER
:=
-
20993
;
2 - Déclarez une exception débutant par les caractères E_ :
/* Signale que les détails d'une séquence sont déjà en cours de modification.*/
e_seq_modification_detail EXCEPTION
;
3 - Puis déclarez la pragma suivante, cette instruction permet d'associer un numéro d'erreur à une variable d'exception ( bloc, procédure ou package ) :
PRAGMA
EXCEPTION_INIT
(
e_seq_modification_detail, -
20993
)
;
4 - Ensuite renseignez la table des erreurs applicatives.
III-C-6. Pour les numéros d'erreur Oracle▲
Il s'agit de tous les numéros qui ne sont pas contenus dans l'intervalle -20000..-20999.
Procédez de la même manière que pour les numéros d'erreur applicative.
c_eresource_busy CONSTANT
PLS_INTEGER
:=
-
54
;
/* Ligne verrouillée, uniquement pour l'instruction : Select for Update NOWAIT */
e_resource_busy EXCEPTION
;
PRAGMA
EXCEPTION_INIT
(
E_RESOURCE_BUSY, -
54
)
;
IV. Gestion des erreurs sous Delphi▲
Nous utiliserons la gestion des exceptions native de Delphi couplée à l'exception EOracleError des composants DOA.
V. Distinction des différentes erreurs▲
Nous distinguerons trois types d'erreurs :
- une erreur applicative Oracle ;
- une erreur Oracle ;
- une erreur Delphi.
V-A. erreurs applicatives Oracle▲
Elles concernent les erreurs contrôlées de l'application dans les traitements en PL/SQL.
V-B. Erreurs Oracle▲
Elles concernent les erreurs non contrôlées de l'application dans les traitements en PL/SQL, les problèmes liés au réseau ou des problèmes particuliers sur le serveur Oracle.
Certaines exceptions Oracle au sein d'un traitement PL/SQL, par exemple l'insertion de ligne violant les contraintes d'intégrité référentielle, peuvent être interceptées afin de transformer cette exception en une exception applicative qui permettra d'informer l'utilisateur que l'opération demandée n'est pas possible.
Dans les autres cas, l'exception déclenchée sera bien un bug de l'application côté serveur. Dans ce cas l'utilisateur reçoit un message d'erreur générique, seul le support au travers des fichiers de log retrouvera exactement le message Oracle d'origine.
V-C. Erreurs Delphi▲
Elles concernent les erreurs, contrôlées ou non, de l'application pour les traitements côté client.
VI. Mise en œuvre▲
La propriété UseMessageTable du composant TOraclaeDataSet.OracleDictionary doit être à false !
Pour distinguer les erreurs Oracle/Delphi des erreurs applicatives déclenchées sous PL/SQL nous utiliserons dans l'unité FError un type de donnée intervalle délimitant les numéros d'erreurs applicatives autorisés.
Type
NumeroErreurApplicativeOracle = 20000
..20999
;
Attention, la directive $R doit être activée.
La suite de composants DOA propose une exception spécifique nommée EOracleError. Elle contient la propriété, en lecture seule, ErrorCode. On peut donc connaître exactement le type d'erreur Oracle survenue lors d'un traitement basé.
À la différence que sous Oracle les erreurs applicatives sont négatives, mais positives sous DOA.
La fonction isErreurApplicativeOracle permet de déterminer si l'erreur Oracle est ou non une erreur applicative.
Function
isErreurApplicativeOracle(ErrorCode: Integer
):Boolean
;
var
ErreurOracle: NumeroErreurApplicativeOracle;
begin
Result:=True
;
try
// Cette affectation provoquera une exception
// si le numéro d'erreur n'est pas une erreur applicative
ErreurOracle:=ErrorCode;
Except
Result:=False
;
end
;
end
;
La procédure AnalyseException se contente de déclencher, selon le type d'erreur, l'exception Delphi appropriée et renseigne selon le cas :
- soit le message utilisateur avec la chaîne contenue dans l'exception déclenchée par le serveur Oracle ;
- soit le message utilisateur passé en paramètre et le message destiné au support ( pile d'appel + message Oracle).
Procedure
AnalyseException(E: Exception;UserMessage:string
);
Begin
try
FrmErreur.Memo1.Clear;
if
(E is
EOracleError) then
// Il s'agit d'une erreur Oracle DOA
if
isErreurApplicativeOracle((E as
EOracleError).ErrorCode ) then
// Affiche le message de l'erreur applicative
begin
FrmErreur.Memo1.Text:=FormatUserMsgError(E.Message
);
exit;
end
;
// Affiche le message d'erreur d'Oracle ou de Delphi
FrmErreur.Memo1.Text:=userMessage+#13#10
+AnsiReplaceText(E.Message
, #10
, #13#10
);
Finally
FrmErreur.Show;
end
;
end
;
La fonction FormatUserMsgError supprime du message d'erreur Oracle la partie située en début de chaîne 'ORA-xxxxx : ' et supprime également les informations de trace ajoutées par Oracle.
function
FormatUserMsgError(MsgError : string
):string
;
{-----------------------------------------------------------------------------
Formate le message d'erreur utilisateur
MsgError : Message d'erreur destiné à l'utilisateur
Renvoie Le message débarrassé des informations inutiles.
-----------------------------------------------------------------------------}
const
Header_msg='ORA-'
; // un message d'erreur Oracle débute par 'ORA-'
lg_Header_msg=11
; // suivi de 5 chiffres 'ORA-12345' et enfin des 2 caractères ': ' et ce jusqu'à la version 9iR2
Stack_msg='ORA-06512'
;
var
Pos_Stack_msg:Integer
;
begin
Try
//Supprime l'entête d'un message d'erreur Oracle
If
Pos(Header_msg, MsgError)=1
then
Delete(MsgError,1
,lg_Header_msg);
// Supprime les informations ajoutées par le Serveur Oracle tels que
// ORA-06512: à "LAURENT.P_MAKE_ERREUR", ligne 57
// ORA-06512: à ligne 2
//
//ORA-06512 at string line string
// Cause: Backtrace message as the stack is unwound by unhandled exceptions.
Pos_Stack_msg:=Pos(Stack_msg, MsgError);
if
Pos_Stack_msg<>0
// -1 pour le caractère #10 ajouté par Oracle
then
Delete(MsgError,Pos_Stack_msg-1
,Length(MsgError));
finally
Result:=AnsiReplaceText(MsgError, #10
, #13#10
);
end
;
end
;
VII. Utilisation▲
VII-A. Exemple de déclaration dans un script PL/SQL▲
c_eseq_modification_detail CONSTANT
PLS_INTEGER
:=
-
20993
;
e_seq_modification_detail EXCEPTION
;
PRAGMA
EXCEPTION_INIT
(
e_seq_modification_detail, -
20993
)
;
Exemple de ligne dans la table Oracle MESSAGES_APPLICATIFS, pour l'erreur (-20991,'TEST') la ligne est :
-20991,'TEST',"Vous ne pouvez pas %1 plus de %2 lignes en même temps. "
VII-B. Exemple d'utilisation sous PL/SQL▲
Dans cet exemple, il s'agit d'une règle de gestion, on teste une borne qui détermine si une opération est possible. Si elle ne l'est pas dans ce cas on déclenche une erreur applicative.
Ce type d'exception est traité, dans notre cas, dans le bloc d'exception OTHERS d'une procédure Oracle.
-- Plus aucune ligne à copier, on quitte
IF
p_tb_ligne.COUNT
=
0
THEN
RAISE
E_Liste_Vide;
END
IF
;
VII-C. Le bloc de traitement des exceptions▲
Dans le bloc d'exception suivant, on traite deux exceptions nommées, E_Liste_vide qui est une erreur applicative et la seconde E_Resource_Busy qui est une erreur du moteur Oracle. On peut donc effectuer des traitements avant de propager l'exception vers le client Delphi. Le troisième cas OTHERS prend en charge tous les cas que l'on n'a pas prévus ou ceux prévus, mais qui ne nécessitent pas de traitements.
/* Bloc d'exception d'une procédure ou fonction PL/SQL */
EXCEPTION
WHEN
E_Liste_vide THEN
ROLLBACK
; -- Libère les verrous précédemment posés
WHEN
PK_EXCEP.E_Resource_Busy THEN
ROLLBACK
;
PK_EXCEP.P_Declenche_Erreur(
c_ESEQ_Modification_Detail,'LISA'
,'L''équipier'
,'copier'
)
;
WHEN
OTHERS
THEN
-- On trace
PK_EMISSION.P_TRACE_ERROR(
L_DEBUG, L_NOM_PROC, SQLCODE
, SUBSTR
(
SQLERRM
,1
,150
))
;
-- et on redéclenche l'exception pour informer le client.
RAISE
;
END
;
VII-D. Utilisation de la procédure P_Declenche_Erreur▲
PROCEDURE
P_DECLENCHE_ERREUR
(
P_NUMERO_ERREUR IN
MESSAGES_APPLICATIFS.CODE_ERREUR%
TYPE
,P_NOM_APPLICATION MESSAGES_APPLICATIFS.APPLICATION%
TYPE
,P_SPECIFICATEUR1 IN
VARCHAR2
:=
'NULL'
,P_SPECIFICATEUR2 IN
VARCHAR2
:=
'NULL'
,P_SPECIFICATEUR3 IN
VARCHAR2
:=
'NULL'
)
IS
--|==================================================
--| P_DECLENCHE_ERREUR :
--|==================================================
--|==================================================
--| F_GET_MSG_ERR :
--|==================================================
--| Objet : Déclenche l'instruction Raise_Application_Error
--| Encapsule la gestion des messages basés.
--|
--| Entrée: P_Numero_Erreur Numéro d'erreur à retrouver
--| P_Nom_Application Nom de l'application concernée
--| P_Specificateur1 Chaîne en correspondance avec %1
--| P_Specificateur2 Chaîne en correspondance avec %2
--| P_Specificateur3 Chaîne en correspondance avec %3
--|
--| Sortie: N/A
--|==================================================
--| 08/01/05 -> L.DARDENNE : original
--|==================================================
BEGIN
RAISE_APPLICATION_ERROR(
P_Numero_Erreur, F_GET_MSG_ERR(
P_Numero_Erreur,
P_Nom_Application,
P_Specificateur1,
P_Specificateur2,
P_Specificateur3))
;
END
;
VII-E. Client de test Delphi▲
Le code de gestion des exceptions lors d'un appel de procédure stockée est le suivant :
procedure
TMainForm.GenereErreur(ANumErreur:Integer
);
var
Requete:TOracleQuery;
Begin
try
try
Requete:=TOracleQuery.Create(Nil
);
with
Requete do
begin
Session := MainSession;
SQL.Add('Begin'
);
SQL.Add(' P_MAKE_ERREUR(:P_NUM_ERREUR);'
);
SQL.Add('End;'
);
DeclareVariable('P_NUM_ERREUR'
, otInteger);
SetVariable('P_NUM_ERREUR'
, ANumErreur);
Execute;
end
;
Finally
Requete.Free;
end
;
Except
on
E:Exception do
AnalyseException(E,'Problème lors de l''appel de procédure stockée.'
);
end
;
end
;
Le client de test permet de générer les trois types d'erreurs et de visualiser l'affichage correspondant.
Il va sans dire que ce client peut être amélioré, par exemple l'ajout d'un flag /Debug sur la ligne de commande permettrait d'afficher le message d'erreur complet ou encore d'afficher la pile d'appel Delphi (cf. les unités JclDebug et JclHookExcept de la JCL), voire celle d'Oracle en utilisant la procédure DBMS_UTILITY.format_call_stack.