Académique Documents
Professionnel Documents
Culture Documents
Réalisé par :
Mr Adnane AL ECHCHEIKH EL ALAOUI
Mr Alaa Eddine JADDAD
Mr Adil SALIM
Encadré par : Mr B. RIYAMI
Matière : SGBD
Année universitaire 2009/2010
2
Sommaire
1 Introduction ..................................................................................................................................................... 4
2 Généralités ...................................................................................................................................................... 5
2.1 Définition ............................................................................................................................................... 5
2.2 À quoi sert un déclencheur ? ................................................................................................................. 5
2.3 Triggers dans SQL3 ............................................................................................................................... 6
2.4 Database trigger ..................................................................................................................................... 6
2.5 Mécanisme général ................................................................................................................................ 7
2.6 Utilisation des triggers ........................................................................................................................... 7
2.6.1 Mise à jour de données ...................................................................................................................... 7
2.6.2 Duplication des données.................................................................................................................... 7
2.6.3 Intégrité étendue des données............................................................................................................ 8
2.6.4 Sécurité étendue des données ............................................................................................................ 8
2.6.5 Ou encore… ...................................................................................................................................... 8
3 Conception des triggers ................................................................................................................................... 9
4 Types des triggers (Oracle) ............................................................................................................................. 9
4.1 Trigger LMD ....................................................................................................................................... 10
4.2 Constitution d’un trigger ..................................................................................................................... 10
4.2.1 Définition de déclenchement ........................................................................................................... 10
4.2.2 Syntaxe ............................................................................................................................................ 10
4.2.3 Option FOR EACH ROW ............................................................................................................... 12
4.2.4 Restriction de déclenchement .......................................................................................................... 12
4.2.5 Action à déclencher ......................................................................................................................... 12
4.2.6 Création d’un trigger LMD ............................................................................................................. 12
4.2.7 Utilisation des pseudos colonnes ..................................................................................................... 15
4.2.8 Regroupement d’événements déclencheurs .................................................................................... 16
4.2.9 Modification d'un trigger................................................................................................................. 16
4.2.10 Suppression d'un trigger.............................................................................................................. 16
4.2.11 Vues du dictionnaire ................................................................................................................... 16
4.2.12 Lecture inconsistante, table en mutation et transaction autonome .............................................. 17
4.2.13 Trigger INSTEAD OF ................................................................................................................ 17
5 Conclusion .................................................................................................................................................... 18
3
1 Introduction
Traditionnellement les bases de données sont des systèmes s'occupant de données
statiques. Les SGBD sont responsables de la gestion de données sous forme de tables,
respectant des règles prédéfinies dans leurs propres structures. Les systèmes
assurent une gestion des données sécurisées et supportant des accès concurrents
grâce à une parfaite maîtrise des transactions. La large diffusion du modèle client‐
serveur pour les bases de données est principalement due à la maturité des SGBD
actuels. L'autonomie du SGBD (sécurité, règle et transaction) a permis la mise en
place du concept du serveur de données; le serveur fourni aux différents producteurs
ou consommateurs de données des informations sécurisées et fiables avec un
maximum de disponibilité. La montée en puissance du "service" gestion des données
peut être assurée assez simplement par un passage à l'échelle du même système.
L'efficacité des serveurs sous les fortes sollicitations nous permet de formuler de
nouvelles exigences et de constater que les bases de données actuelles sont capables
de comportements dynamiques.
Prenons par exemple une base de données gérant un stock de pièces et pouvant
comporter dans son schéma une règle permettant son réapprovisionnement à partir
d'un seuil prédéfini.
Ce système nécessite deux processus distincts, soit la détection du seuil minimum, et
l'action de réapprovisionnement elle‐même, en suivant un processus prédéfini (une
règle métier).
L'événement doit être catalogué dans la base elle‐même pour permettre sa détection
indépendamment du client. Sa puissance de description dépend des possibilités du
SGBD utilisé.
Quant à l'action, elle doit pouvoir être décrite avec un langage de programmation
complet permettant la manipulation native des données. Idéalement, ce langage doit
être capable d'intégrer SQL pour la gestion des données et permettre une
programmation classique pour assurer la complétude du langage. Le langage,
suffisamment puissant, permettrait une interaction avec les autres services du
système, soit avec un dialogue SQL, soit avec des dialogues de genre RPC.
Afin d’assurer ces comportements dynamiques, les SGBD actuels permettent
l’utilisation de déclencheurs.
4
2 Généralités
2.1 Définition
Un déclencheur (trigger) est une action stockée dans la base et associée à une table.
La suite d'instructions que constitue cette action est exécutée automatiquement par
le système de gestion de base de données, chaque fois que l'événement auquel elle
est associée se produit.
Les déclencheurs (triggers) existent depuis la version 6 d’Oracle. Ils sont compilables
depuis la version 7.3 (auparavant, ils étaient évalués lors de l’exécution). Depuis la
version 8, il existe un nouveau type de déclencheur (INSTEAD OF) qui permet la mise
à jour de vues multi tables. La plupart des déclencheurs peuvent être vus comme des
programmes résidents associés à un événement particulier (insertion, modification
d’une ou de plusieurs colonnes, suppression) sur une table (ou une vue). Une table
(ou une vue) peut « héberger » plusieurs déclencheurs ou aucun. Nous verrons qu’il
existe d’autres types de déclencheurs que ceux associés à une table (ou à une vue)
afin de répondre à des événements qui ne concernent pas les données. À la
différence des sous‐programmes, l’exécution d’un déclencheur n’est pas
explicitement opérée par une commande ou dans un programme, c’est l’événement
de mise à jour de la table (ou de la vue) qui exécute automatiquement le code
programmé dans le déclencheur. On dit que le déclencheur « se déclenche » (l’anglais
le traduit mieux : fired trigger). La majorité des déclencheurs sont programmés en
PL/SQL (langage très bien adapté à la manipulation des objets Oracle), mais il est
possible d’utiliser un autre langage (C ou Java par exemple).
2.2 À quoi sert un déclencheur ?
Un déclencheur permet de :
● Programmer toutes les règles de gestion qui n’ont pas pu être mises en place par
des contraintes au niveau des tables. Par exemple, la condition : une compagnie ne
fait voler un pilote que s’il a totalisé plus de 60 heures de vol dans les 2 derniers mois
sur le type d’appareil du vol en question, ne pourra pas être programmée par une
contrainte et nécessitera l’utilisation d’un déclencheur.
● Déporter des contraintes au niveau du serveur et alléger ainsi la programma on
client.
● Renforcer des aspects de sécurité et d’audit.
● Programmer l’intégrité référen elle et la réplica on dans des architectures
distribuées avec l’utilisation de liens de bases de données (database links).
5
2.3 Triggers dans SQL3
Les triggers de base de données sont normalisés par la norme SQL3.
La norme SQL2 décrivait la notion d'assertion, qui ressemble à celle du trigger. Une
assertion est une règle non rattachée à une table, qui doit être respectée, quelles que
soient les instructions envoyées au système.
Exemple :
Les SGBD du marché implémentent la notion de "database trigger". Cette notion, non
normalisée engendre des objets relativement similaires chez les différents
constructeurs.
Les triggers sont généralement des objets, appartenant à une table, permettant
d'exécuter une procédure lorsqu'un événement prédéfini est produit sur cette même
table.
La définition du déclencheur est similaire d'un SGBD à l'autre, mais l'action, décrite
dans un langage de programmation souvent propriétaire du SGBD, est totalement
dépendante de son pouvoir de description.
Il faut remarquer encore que la liste exhaustive des déclencheurs supportés par le
SGBD peut être différente d'un système à l'autre, mais une nouvelle version du même
SGBD peut supporter de nouveaux événements.
En bref, les triggers sont des procédures stockées dans la base de données et
implicitement exécutées ou tirées (triggered) quand un évènement se produit.
Traditionnellement l'événement est une écriture (INSERT UPDATE, DELETE) dans une
table.
6
2.5 Mécanisme général
La figure suivante illustre les étapes à suivre pour mettre en œuvre un déclencheur. Il
faut d’abord le coder (comme un sous‐programme), puis le compiler (il sera stocké
ainsi en base).
Par la suite, au cours du temps, et si le déclencheur est actif (nous verrons qu’il est
possible de désactiver un déclencheur même s’il est compilé), chaque événement
(qui caractérise le déclencheur) aura pour conséquence son exécution.
Les triggers servent à garantir l'exécution d'une tâche et à centraliser les opérations
sans s'occuper de qui les déclenchent ni d'où elles se déclenchent puisqu'elles se
trouvent dans la base de données.
Lors de l'effacement de données, on peut stocker automatiquement les valeurs
effacées dans une table journal. L'effacement devient alors logique. Une autre
utilisation classique est la gestion de champs dérivés (champs calculés).
7
La duplication de données, pour des raisons de performance sur une base distribuées
peut être faite avec des triggers. On peut facilement gérer une réplication synchrone
asymétrique.
La gestion de l'intégrité des données peut être faite avec des triggers,
particulièrement dans les cas où elle ne peut être assurée par des contraintes
déclaratives. Toutefois, les triggers ne doivent pas se substituer au système
d’intégrité natif du SBBD pour des raisons de portabilité.
Il est important de noter que si les triggers peuvent gérer l'ensemble de l’intégrité
des données de la base, mais les contrôles s'appliquent au niveau de la seule
transaction. Ils ne contrôlent donc pas les données existantes.
On peut également gérer les droits d'accès aux données avec des triggers,
particulièrement si ceux‐ci ne peuvent être traités par les commandes classiques de
gestion des droits.
2.6.5 Ou encore…
• Audit complexe
• Prévention de transactions invalides
• Renforcement de règles de gestion complexes
• Mise en place d'évènements de connexion transparents
• Génération automatique de colonnes dérivées
• Mise en place de vues modifiables complexes
• Réplication synchrone symétrique de données entre un système legacy et un
nouveau système en cours de développement
• Surveillance d'évènement systèmes
8
3 Conception des triggers
Il est important de suivre les quelques règles ci‐dessous lors du design des triggers.
• Utiliser les triggers pour exécuter des opérations de façon centralisée, ceci
quelque soit le genre de client ou d’application connectée.
• Limiter la taille du corps du trigger à 60 lignes de code au maximum. Si la
taille doit être dépassée, utiliser le trigger pour appeler une procédure stockée
(le corps d’un trigger ne peut dépasser 32 Ko).
• Ne pas développer des triggers qui duplifient des fonctionnalités existante du
SGBD (ex: Intégrité référentielle).
• Ne pas créer des triggers récursifs !!!! (Exécution de l'action qui déclenche le
trigger dans le corps).
• Utiliser judicieusement les triggers sur la base de données. Ils sont exécutés
pour chaque utilisateur chaque fois que l'évènement se produit.
• Respecter les transactions !
• Utiliser avec précaution (difficile à debugger et à maintenir…)
Un trigger peut être de plusieurs types:
• Trigger LMD sur une table
• Trigger INSTEAD OF sur une vue
• Trigger système sur DATABASE ou SCHEMA
Remarque :
Les événements déclencheurs peuvent être :
● Une instruction INSERT, UPDATE, ou DELETE sur une table (ou une vue). On parle
de déclencheurs LMD ;
● Une instruction CREATE, ALTER, ou DROP sur un objet (table, index, séquence, etc.).
On parle de déclencheurs LDD ;
9
● le démarrage ou l’arrêt de la base (startup ou shutdown), une erreur spécifique
(NO_DATA_ FOUND, DUP_VAL_ON_INDEX, etc.), une connexion ou une déconnexion
d’un utilisateur.
On parle de déclencheurs d’instances.
Les triggers du LMD sont les plus courants. On les trouve presque systématiquement
chez chaque constructeur.
La constitution d'un trigger est différente d'un constructeur à l'autre, mais on
retrouve globalement les mêmes éléments dans les différentes syntaxes.
Un trigger est constitué de trois parties distinctes:
Cette partie déclare le trigger en lui donnant un identificateur (unique au schéma)
ainsi qu'un événement déclencheur.
Cet événement déclencheur est une écriture (INSERT UPDATE, DELETE) dans la table
qui lui est associée.
On définit également si le trigger sera déclenché avant ou après l'écriture dans la
table avec les options BEFORE et AFTER.
4.2.2 Syntaxe
Remarque: Conventions d’écriture.
Exemples, syntaxe
« MAJUSCULES » Commandes SQL
« Italiques » Variables de substitution
[ ] Elément facultatif
{ } Liste de choix
¦ Séparateur dans une liste de choix
NB : Les exemples et syntaxes de ce support de cours sont spécifiques à la base
de données Oracle 10g. Nous spécifierons lorsqu’un exemple fait référence à la
norme.
Pour pouvoir créer un déclencheur dans votre schéma, vous devez disposer du
privilège CREATE TRIGGER (qui est inclus dans le rôle RESOURCE mais pas dans
CONNECT). Pour créer un déclencheur dans un autre schéma, le privilège CREATE
ANY TRIGGER est requis.
10
En plus de ces conditions, pour fabriquer un déclencheur d’instances, il faut détenir le
privilège ADMINISTER DATABASE TRIGGER.
Un déclencheur est composé de trois parties : la description de l’événement traqué,
une éventuelle restriction (condition) et la description de l’action à réaliser lorsque
l’événement se produit. La syntaxe de création d’un déclencheur est la suivante :
CREATE [OR REPLACE] TRIGGER [schéma.] nomDéclencheur
{ BEFORE | AFTER | INSTEAD OF }
{ { DELETE | INSERT | UPDATE [OF col1 [,col2]…] }
[OR { DELETE | INSERT | UPDATE [OF col1 [,col2]…] }]…
ON { [schéma.] nomTable | nomVue }
[REFERENCING
{ OLD [AS] nomVieux | NEW [AS] nomNew | PARENT [AS] nomParent }
[ OLD [AS] nomVieux | NEW [AS] nomNew | PARENT [AS] nomParent]… ]
[FOR EACH ROW] }
|
{ événementBase [OR événementBase]… |
actionStructureBase [OR actionStructureBase]… }
ON { [schéma.] SCHEMA | DATABASE } }
[WHEN ( condition ) ]
{ Bloc PL/SQL (variables BEGIN instructions END ; )
| CALL nomSousProgramme(paramètres) }
Les options de cette commande sont les suivantes :
● BEFORE | AFTER | INSTEAD OF précise la chronologie entre l’ac on à réaliser par le
déclencheur LMD et la réalisation de l’événement (exemple BEFORE INSERT
programmera l’exécution du déclencheur avant de réaliser l’insertion).
● DELETE | INSERT | UPDATE précise la nature de l’événement pour les déclencheurs
LMD.
● ON {[schéma.] nomTable | nomVue} spécifie la table, ou la vue, associée au
déclencheur LMD.
● REFERENCING permet de renommer des variables.
● FOR EACH ROW différencie les déclencheurs LMD au niveau ligne ou au niveau état.
● événementBase iden fie la nature d’un déclencheur d’instance (STARTUP ou
SHUTDOWN
pour exécuter le déclencheur au démarrage ou à l’arrêt de la base), d’un déclencheur
d’erreurs (SERVERERROR ou SUSPEND pour exécuter le déclencheur dans le cas d’une
erreur particulière ou quand une transaction est suspendue) ou d’un déclencheur de
connexion (LOGON ou LOGOFF pour exécuter le déclencheur lors de la connexion ou
de la déconnexion à la base).
● ac onStructureBase spécifie la nature d’un déclencheur LDD (CREATE, ALTER,
11
DROP, etc. pour exécuter par exemple le déclencheur lors de la création, la
modification ou la suppression d’un objet de la base).
● ON {[schéma.]SCHEMA | DATABASE}} précise le champ d’applica on du
déclencheur (de type LDD, erreur ou connexion). Utilisez DATABASE pour les
déclencheurs qui s’exécutent pour quiconque commence l’événement, ou SCHEMA
pour les déclencheurs qui ne doivent s’exécuter que dans le schéma courant.
● WHEN condi onne l’exécu on du déclencheur.
Cette option définit si le trigger est un trigger de ligne ou d'instruction. S’il est définit
comme trigger de ligne, le corps du trigger sera exécuté pour chaque tuple "touché"
par l'instruction qui a déclenché le trigger. Si le trigger est un trigger d'instruction, il
sera déclenché une et une seule fois, indépendamment du nombre de tuples
manipulés par l'instruction.
La plupart des triggers sont spécifiés avec l’option FOR EACH ROW dans la pratique.
On l'appelle aussi corps du trigger. C'est l'action à exécuter lorsque l'événement
associé se produit.
Cette suite d'instructions est écrite dans un langage supporté par la base de données.
Oracle supporte en natif son propre langage appelé PL/SQL, ainsi que l’appel de
procédures compilées écrites dans un autre langage (C, C++, Java,...)
Pour ce type de déclencheurs, l’événement à déterminer est une mise à jour
particulière de la base (ajout, modification ou suppression dans une table ou une
vue). L’exécution est dépendante ou non du nombre de lignes concernées par
l’événement. On programme un déclencheur de lignes (row trigger) quand on désire
exécuter autant de fois le déclencheur qu’il y a de lignes concernées par une mise à
jour. Si on désire exécuter une seule fois le déclencheur quel que soit le nombre de
lignes concernées, on utilisera un déclencheur d’état (statement trigger).
La directive FOR EACH ROW distingue ces deux familles de déclencheurs.
12
Dans l’exemple d’une table t1 ayant cinq enregistrements, si on programme un
déclencheur de niveau ligne avec l’événement AFTER DELETE, et qu’on lance DELETE
FROM t1, le déclencheur exécutera cinq fois ses instructions (une fois après chaque
suppression). Le tableau suivant explique ce mécanisme :
Un déclencheur de lignes est déclaré avec la directive FOR EACH ROW. Ce n’est que
dans ce type de déclencheur qu’on a accès aux anciennes valeurs et aux nouvelles
valeurs des colonnes de la ligne affectée par la mise à jour prévue par l’événement
Considérons l’exemple suivant, et programmons la règle de gestion tout pilote ne
peut être qualifié sur plus de trois types d’appareils. Ici, il s’agit d’assurer la
cohérence entre la valeur de la colonne nbQualif de la table Pilote et les lignes de la
table Qualifications. Programmons le déclencheur TrigInsQualif qui surveille les
insertions arrivant sur la table Qualifications et incrémente de 1 la colonne nbQualif
pour le pilote concerné, ou refuse l’insertion pour le pilote ayant déjà trois
qualifications (cas du pilote de code 'PL‐1' dans la figure suivante).
L’événement déclencheur est ici BEFORE INSERT car il faudra s’assurer, avant de faire
l’insertion, que le pilote n’est pas déjà qualifié sur trois types d’appareils. On utilize
un déclencheur FOR EACH ROW car on désire qu’il s’exécute autant de fois qu’il y a
de lignes concernées par l’événement déclencheur. S’il se produit une insertion
multiple de type INSERT INTO Qualifications SELECT…, on préfère lancer plusieurs fois
le déclencheur.
Chaque enregistrement qui tente d’être ajouté dans la table Qualifications est
désigné par :NEW au niveau du code du déclencheur. L’accès aux colonnes de ce
pseudo‐enregistrement dans le corps du déclencheur se fait par la notation pointée.
13
Le code minimal de ce déclencheur (on ne prend pas en compte l’éventuelle erreur
du SELECT ne renvoyant aucun pilote). est décrit dans le tableau suivant :
Code PL/SQL Commentaires
CREATE TRIGGER TrigInsQualif Déclaration de l’événement
BEFORE INSERT ON Qualifications déclencheur
FOR EACH ROW
DECLARE Déclaration des variables
v_compteur Pilote.nbHVol%TYPE; locales.
v_nom Pilote.nom%TYPE;
BEGIN Corps du déclencheur.
SELECT nbQualif, nom INTO v_compteur, Extraction et mise à jour
v_nom du pilote concerné par
FROM Pilote WHERE brevet = ; la qualification.
IF v_compteur < 3 THEN Renvoi d’une erreur
UPDATE Pilote SET nbQualif = nbQualif + 1 utilisateur.
WHERE brevet = ;
ELSE
(‐20100, 'Le pilote '
|| v_nom || ' a déjà 3 qualifications!');
END IF;
END;
/
Le test de ce déclencheur peut être réalisé sous SQL*Plus comme le montre la trace
suivante.
On retrouve l’erreur utilisateur qui est levée en premier.
Événement déclencheur Sortie SQL*Plus
SQL> INSERT INTO Qualifications 1 ligne créée.
VALUES ('PL‐2', 'A380', SQL> SELECT * FROM Pilote;
'20‐06‐2006'); BREVET NOM NBHVOL COMP NBQUALIF
‐‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐ ‐‐‐‐‐‐ ‐‐‐‐ ‐‐‐‐‐‐‐‐‐
PL‐1 J.M Misztela 450 AF 3
PL‐2 Thierry Guibert 3400 AF
PL‐3 Michel Tuffery 900 SING 1
SQL> INSERT INTO Qualifications ERREUR à la ligne 1 :
VALUES ('PL‐1', 'A380', ORA‐06512: à "SOUTOU.TRIGINSQUALIF",
'20‐06‐2006'); ligne 9
ORA‐04088: erreur lors d'exécution du
déclencheur 'SOUTOU.TRIGINSQUALIF'
14
Exemple de création d’un trigger :
CREATE TRIGGER dept_del_cascade
AFTER DELETE ON dept
FOR EACH ROW
BEGIN
DELETE FROM emp
WHERE emp.deptno = :old.deptno;
END
CREATE TRIGGER [schéma.]trigger
{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [OF colonne [,colonne]...]}
[OR {DELETE | INSERT | UPDATE [OF colonne [,colonne]...]}...
ON [schema.]table
[[REFERENCING {OLD [AS] ancien NEW [AS] nouveau ]
| NEW [AS] nouveau OLD [AS] ancien ]}]
FOR EACH ROW]
[ENABLE|DISABLE]
[WHEN (condition )]
bloc_PL/SQL
Les triggers de lignes (FOR EACH ROW) permettent de manipuler les attributs
(colonnes) du tuple actuellement traités, avec les pseudos colonnes :New et :Old.
Les triggers d'insertion ont accès à une variable contenant la nouvelle valeur d'une
colonne ( :New.colonne).
Les triggers de suppression ont accès à une variable contenant l'ancienne valeur
d'une colonne (:Old.colonne).
Les triggers de modification ont accès aux deux variables précédentes.
Vous pouvez spécifier un nom différent pour :New et :Old avec la clause
REFERENCING (peu utilisé dans la pratique). Vous pouvez ainsi, dans le corps du
trigger, utiliser les nouveaux noms à la place des :New et :Old.
15
4.2.8 Regroupement d’événements déclencheurs
On peut créer un trigger traitant le cas de plusieurs ordres LMD. La condition de
déclenchement sera donc composée :
La modification d'un trigger consiste à l'activer ou le désactiver. La recompilation d'un
trigger peut également se faire par la commande de modification suivante:
Pour supprimer un trigger, l'utilisateur doit formuler la commande suivante:
16
Les vues du dictionnaire référençant les triggers sont: USER_TRIGGERS,
ALL_TRIGGERS et DBA_TRIGGERS
Dans un trigger FOR EACH ROW, on ne peut pas lire ou mettre à jour la table à partir
de laquelle le trigger a été déclenché. Cette limitation permet d’éviter les lectures
inconsistantes.
Cela provoque l’erreur suivante:
L'option INSTEAD OF (littéralement : au lieu de) peut être spécifiée sur les triggers
placé sur une vue.
Cette option offre une possibilité transparente et efficace pour permettre la mise à
jour de données à travers des vues non modifiables (non UPDATABLE.)
L'action du trigger est bien effectuée en lieu et place de l'instruction qui le déclenche.
Les triggers INSTEAD OF ne sont évidement pas utilisable sans l'option FOR EACH
ROW et ne disposent pas des options BEFORE et AFTER.
La syntaxe des triggers INSTEAD OF est la suivante :
CREATE TRIGGER nom_trigger INSTEAD OF
{INSERT¦UPDATE¦DELETE}
ON nom_de_vue
suite_d_instructions;
17
5 Conclusion
Ce mini‐projet nous a permis de perfectionner nos connaissances en
programmation avancée en PL/SQL et surtout la partir des Déclencheurs
automatiques en utilisant Base de données Oracle
Suite à notre avancé, dans ce mini‐projet nous avons fait face à plusieurs
difficulté au niveau de langage PL /SQL ce qui nous a poussé à faire des recherches
pour concrétiser certaines idées et aussi a demandé l’aide de notre cher professeur
qui nous l’a accordé avec beaucoup de bonne volonté et a qui nous sommes très
reconnaissant.
Par la suite nous aimerions cerner toutes les subtilités du ORACLE, pour
améliorer et évolué nos connaissances, dans le but de devenir des ingénieurs
compétant.
18