View
103
Download
0
Category
Preview:
Citation preview
GIS882
Gestion des erreurs
Packages©Alain Villeneuve, 1999
Erreurs dans Forms
FORMS Une erreur (exception) peut survenir dans:
instruction SQL
instruction PL/SQL
un module PL/SQL appelé par un autre
Lorsqu'une erreur n'est pas gérée au niveau où elle survient, elle sera propagée au niveau supérieur
Le développeur peut manipuler lui-même l'exception et la rendre plus conviviale
Par défaut, à moins de gestion d'erreur, le reste du code est exécuté
Triggers dans FormsTriggers d'application dans FORMS Oracle va essayer de récupérer le traitement suivant
une erreur dans un trigger d'application Le comportement par défaut est défini dans
l'environnement FORMS (voir manuel de référence) Exemples de triggers:
– Pre-item– On-insert– When-validate-item– When-button-pressed– When-mouse-click
triggers au niveau du formulaire, du block, ou de l'item
Triggers Forms: stratégies
Triggers d'application dans FORMS: 3 stratégies écrire un gestionnaire complet d'exception laisser le traitement par défaut à Oracle ne gérer que les exceptions les plus critiques pour vous
Fonctions disponibles ERROR_TYPE(retourne CHAR) ERROR_CODE(retourne NUMBER) ERROR_TEXT(retourne CHAR) DBMS_ERROR_CODE(retourne NUMBER) DBMS_ERROR_TEXT(retourne CHAR)
Erreurs internes dans Forms
Erreurs dans les routines internes (built-ins) au delà d'une centaine de routines internes exemples:
GO_BLOCK ('nom_du_bloc');
GO_ITEM ('nom_item'); FORM_SUCCESS (retourne un booléen) FORM_FAILURE (retourne un booléen) FORM_FATAL (retourne un booléen) on signale l'erreur par RAISE FORM_TRIGGER_FAILURE
dans le code; cette exception n'a pas à être définie dans votre code
Built-ins dans Forms
Erreurs dans les routines internes (built-ins)
Exemple:/* when_button_pressed trigger */
GO_BLOCK('bloc_quelconque');
IF NOT FORM_SUCCESS THEN
RAISE Form_Trigger_Failure;
END IF;
END;
Si le bloc désigné n'existe pas par exemple, alors le GO_BLOCK sera fautif et le FORM_SUCCESS sera à FALSE
Chapitre Handling errors de Forms
PACKAGES
Sert à regrouper un ensemble d'objets, de types de données et de sous-programmes (fonctions et procédures) qui sont logiquement apparentés
consiste en deux grandes sections– la spécification du package– le corps du package
la spécification est l'interface visible du package– sert à déclarer les types, variables, constantes, exceptions,
curseurs, et sous-programmes disponibles
le corps contient le code programmé en soi
Packages (2)
Un package est différent d'un programme normal contenant des sous-programmes
en essence, les deux types de construction se ressemblent mais le package a la particularité de permettre l'adressage à partir de l'externe des procédures qui le composent
une procédure qui contient des sous-programmes (d'autres procédures) ne permet pas d'atteindre directement ces sous-programmes de l'extérieur
le package au contraire permettra sous certaines conditions de rendre ses sous-programmes visibles
Exemple de packagePACKAGE nom_package IS
/* déclaration des variables, cursors, etc
tous ces éléments sont publics et disponibles aux sous-programmes
inclus dans le corps du package
de plus ces déclarations sont visibles à l'environnement appelant */
END [nom_package];
PACKAGE BODY nom_package IS
/* déclaration des variables, cursors, et autres objets privés
codes programmés des sous-programmes */
BEGIN
… -- instructions d'initialisation et autres
END [nom_package];
Les sous-programmes viennent ici!
Avantages
Avantages– programmation modulaire– facilité de développement– protection de l'implantation (information hiding)– fonctionnalités additionnelles à l'application– performance améliorée
Mise en place
CREATE PACKAGE emp_actions AS
/* Définition des types, cursors et exceptions visibles */
TYPE EmpRecTyp IS RECORD (emp_id INTEGER, salary REAL);
TYPE DeptRecTyp IS RECORD (dept_id INTEGER, location VARCHAR2);
CURSOR desc_salary RETURN EmpRecTyp;
salary_missing EXCEPTION;
Spécifications du package: déclaration des variables et objets. Suite de cette section sur la prochaine page.
Spécifications du package
/* Sous-programmes visibles de l'extérieur. */
/* chacun peut être appelé par un autre programme */
FUNCTION hire_employee (
ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER) RETURN INTEGER;
PROCEDURE fire_employee (emp_id INTEGER);
PROCEDURE raise_salary (emp_id INTEGER, increase NUMBER);
FUNCTION nth_highest_salary (n INTEGER) RETURN EmpRecTyp;
END emp_actions;
Suite et fin de la partie spécifications du package. Tous ces éléments seront visibles.
Fin des specs.
Corps du package
PACKAGE BODY emp_actions IS number_hired INTEGER; -- variable visible au package seulement /* Définition du cursor déclaré dans la spécification du package. */CURSOR desc_salary RETURN EmpRecTyp IS SELECT empno, sal FROM emp ORDER BY sal DESC; /* Chacun des sous-programmes sera défini en entier ici */
FUNCTION hire_employee (ename VARCHAR2, job VARCHAR2, mgr NUMBER, sal NUMBER, comm NUMBER, deptno NUMBER)
RETURN INTEGER IS new_empno INTEGER;BEGIN
Début du corps
Corps (2)
SELECT empno_seq.NEXTVAL INTO new_empno FROM dual;
INSERT INTO emp VALUES (new_empno, ename, job,
mgr, SYSDATE, sal, comm, deptno);
number_hired := number_hired + 1;
RETURN new_empno;
END hire_employee;
Ceci marque la fin de la fonction hire_employee et non la fin du package: d'autres sous-programmes ont été déclarés qui doivent être définis avant le END final
Corps (3)
PROCEDURE fire_employee (emp_id INTEGER) IS
BEGIN
DELETE FROM emp WHERE empno = emp_id;
END fire_employee;
Cette procédure sert à retirer un employé de la table emp.
Corps (4)
PROCEDURE raise_salary (emp_id INTEGER, increase NUMBER) IS
current_salary NUMBER;
BEGIN
SELECT sal INTO current_salary FROM emp
WHERE empno = emp_id;
IF current_salary IS NULL THEN
RAISE salary_missing;
ELSE
UPDATE emp SET sal = sal + increase
WHERE empno = emp_id;
END IF;
END raise_salary;
Augmenter le salaire d'un employé par un certain montant
Corps (5)
FUNCTION nth_highest_salary (n INTEGER) RETURN EmpRecTyp IS
emp_rec EmpRecTyp;
BEGIN
OPEN desc_salary;
FOR i IN 1..n LOOP
FETCH desc_salary INTO emp_rec;
END LOOP;
CLOSE desc_salary;
RETURN emp_rec;
END nth_highest_salary;
Retourne le record de l'employé qui a le nième salaire le plus élevé: le n (rang) désiré est fourni comme paramètre de la fonction.
Sont définis dans le package specs.
Corps (6)
/* Define local function, available only in package. */FUNCTION rank (emp_id INTEGER, job_title VARCHAR2) RETURN INTEGER IS/* Return rank (highest = 1) of employee in a given job classification based on performance rating. */ head_count INTEGER; score NUMBER;BEGIN SELECT COUNT(*) INTO head_count FROM emp WHERE job = job_title; SELECT rating INTO score FROM reviews WHERE empno = emp_id; score := score / 100; -- maximum score is 100 RETURN (head_count + 1) - ROUND(head_count * score);END rank;
Cette fonction est privée au package (donc pas visible à l'extérieur) parce qu'elle n'est pas annoncée dans le package specs
Corps (7)
BEGIN -- initialization part starts here
INSERT INTO emp_audit VALUES (SYSDATE, USER, 'EMP_ACTIONS');
number_hired := 0;
END emp_actions;
Entre le BEGIN et le END du corps du package, on placera les instructions d'initialisation requises par le package. Ici, on insère un tuplet dans un table de piste des événements pour fins de contrôle: c'est une idée farfelue puisque le BEGIN du package ne sera exécuté qu'une seule fois soit au premier appel.
Utilisation du package
Appel ou référence à un composant d'un package:
nom_package.nom_objet [paramètres optionnels]
Exécution à partir de SQL*plus:
EXECUTE nom_package.nom_fonction[paramètres]
Exécution à partir de Reports, Forms via PL/SQL
nom_package.nom_fonction[paramètres]
TABLEAUX PL/SQL
Structures de données en mémoire vive ne comportent qu'une seule colonne ne correspondent pas à une table SQL les lignes de la table sont adressées par une valeur
entière du type BINARY_INTEGER taille dynamique: les tables s'agrandissent
automatiquement au besoin en fait, un tableau n'est ni plus ni moins qu'un vecteur on pourra créer autant de tableaux PL/SQL qu'il y a
de colonnes correspondantes dans la table désirée
Définition de tableau
Première étape: définir un type de référenceDECLARE
…
TYPE type_tableau_nom IS TABLE OF CHAR(20) INDEX BY BINARY_INTEGER;
Deuxième étape: définir le tableauNOMS_EMPLOYES TYPE_TABLEAU_NOM;
PRENOMS_EMPLOYES TYPE_TABLEAU_NOM;
Un tableau ne peut pas être initialisé dans sa déclaration
toujours mettre INDEX BY BINARY_INTEGER
Définition
Le %TYPE est aussi supporté dans la déclaration d'un tableau:
DECLARE
…
TYPE type_tableau_nom IS TABLE OF employes.nom%TYPE
INDEX BY BINARY_INTEGER;
Une variable BINARY_INTEGER est requise dans votre DECLARE pour adresser le tableau dans le programmei BINARY_INTEGER := 0; -- peut être initialisé
cette variable servira comme index dans le tableau
Utilisation
Limite de taille:– (-231 -1 à 231 - 1) nombre de cellule– contraint à l'espace RAM réel
Utilisation des variables tableaux– comme toute autre variable simple– il faut toujours spécifier le numéro de cellule désiré, ceci se
fait avec votre variable de type binary_integer
exemplesnom_tableau(index) := valeur;
IF nom_tableau(index) = … THEN
Exemple
Exemple:Supposons un système de gestion des ressources
humaines dans lequel plusieurs codes sont présents dans les dossiers d'employés (statut marital, sexe, statut d'emploi, etc.).
Supposons aussi une table liste_codes qui contient les codes ainsi que leurs descriptions.
Nous voulons imprimer les dossiers étoffés des employés.
Plusieurs centaines d'employés existent dans la table.
Exemple (2)
Alors typiquement, à chaque tuplet en provenance de la table employé, on devra aller chercher la description qui correspond à un code (statut marital), et ce pour chacun des codes.
Cette recherche sera normalement par un SELECT.
Supposons l'existence de 10 colonnes de code par employé et supposons 1000 employés dans la table, alors 10.000 SELECT seront exécutés.
Exemple (3)
Solution: créer un package le package aura une fonction de chargement de tous
les codes dans un tableau PL/SQL le package contiendra une fonction de recherche de
la description d'un code et retournera celle-ci le chargement initial serait dans la section BEGIN du
package: nombre de SELECT totaux = 100 n'oubliez pas que le package est initialisé lors du
premier appel à n'importe laquelle de ses procédures
Exemple (4)
Describe de la table codescodes not null varchar2(8);
description not null varchar2(30);
Pour fins de cet exemple, on pose que tous les codes sont dans cette table de codes et que par exemple les deux premiers caractères de la clé identifie la catégorie de code:SX pour sexe, alors 'SXF', 'SXM' existent
SM pour statut marital, alors 'SMC', 'SMM','SMV', …
DD pour dernier diplôme, alors 'DDSEC5', 'DDDEC','DDBAA',...
Exemple (5)
PACKAGE descriptifs IS
TYPE description_codes IS TABLE OF codes.description%TYPE
INDEX BY BINARY_INTEGER;
TYPE code_code IS TABLE OF codes.code%TYPE
INDEX BY BINARY_INTEGER;
liste_code code_code; --- tableau en soi
liste_description description_codes; -- tableau en soi
nombre NUMBER; -- compteur du nombre de codes
FUNCTION retourne_descriptif(CODE codes.code%TYPE)
RETURN CHAR;
END descriptifs;
Exemple (6)
PACKAGE BODY descriptifs ISCURSOR c_description IS SELECT * FROM codes;FUNCTION retourne_descriptif(CODE codes.code%TYPE)
RETURN CHAR ISi BINARY_INTEGER := 0;
BEGINFOR i IN 1..nombre LOOP
IF descriptifs.liste_code(i) = CODE THEN RETURN descriptifs.liste_description(i)
END IF;END LOOP;RETURN ('Code invalide');END retourne_descriptif;
Lorsque l'on trouve la valeur recherchée, on retourne le descriptif qui lui correspond.
Si fin de la boucle, c'est qu'on n'a rien trouvé!
Exemple (7)
BEGIN -- ceci est le BEGIN du package et ne sera exécuté qu'une fois
nombre := 1;
OPEN c_description;
LOOP
FETCH c_description INTO liste_code(nombre), liste_description(nombre);
EXIT WHEN c_description%NOTFOUND;
nombre := nombre + 1;
END LOOP;
CLOSE c_description;
END descriptifs;
Les tableaux de codes et de descriptions sont chargés. Nombre contient le nombre réel d'entrées dans les tableaux.
Recommended