samedi 30 novembre 2002

Trigger


TRIGGER

Un trigger est associé à une table ou vue et permet d'exécuter du code selon un évènement donné. Tout comme les procédures stockées, tout s'exécute sur le serveur. Le trigger n'est jamais appelé directement. C'est lorsque l'évènement auquel est associé le trigger survient, le code est alors exécuté. Le langage utilisé par la base de données peut être utilisé pour le trigger, point besoin d'être limité à du SQL. Il est donc possible d'effectuer des tâches très complexes très aisément en utilisant ce langage au lieu d'avoir à gérer de complexe et lourde requête SQL.

Il est donc possible de se passer des triggers, mais un plus grand effort sera nécessaire pour parvenir au même objectif. Un trigger est exécuté sur le serveur, il n'y a donc pas de trafic réseau qui est généré telle que lors de requête SQL. Cette centralisation permet que plusieurs applications puissent y avoir accès. Si un changement doit se faire, il n'est pas nécessaire de recompiler l'application. L'utilisation de cette technique peut alors s'avérer beaucoup plus rapide que son équivalent en SQL. Un trigger n'a aucun paramètre d'entré et de sortie.
Il est possible d'utiliser les triggers pour diverses raisons:
  • Gérer l'intégrité des données
  • Gérer les règles d'affaires
  • Sécurité
  • ...

Syntaxe d'un trigger

CREATE TRIGGER name FOR { table | view}
[ACTIVE | INACTIVE]
{BEFORE | AFTER} {DELETE | INSERT | UPDATE}
[POSITION number]
AS < trigger_body >
< trigger_body > = [< variable_declaration_list >] < block >
< variable_declaration_list > =DECLARE VARIABLE variable < datatype >;
[DECLARE VARIABLE variable < datatype > ;...]
< block > =
BEGIN
< compound_statement > [< compound_statement >...]
END
< compound_statement > = {< block > | statement;}

Il existe tel qu'on peut le voir certaines ressemblances avec la syntaxe des procédures stockées

Opérateur

Les conditions et les boucles sont identiques à celles qu'on a pu voir dans les procédures stockées
Un trigger peut être exécuté pour les évènements suivants
  • BeforeInsert
  • BeforeUpdate
  • BeforeDelete
  • AfterInsert
  • AfterUpdate
  • AfterDelete
Lors du tutoriel des Générateurs, nous avons rapidement vu le code d'un trigger
SET TERM ^;
CREATE TRIGGER TRIG_CLIENT_GenId FOR CLIENT
ACTIVE BEFORE INSERT AS
BEGIN
  IF ( NEW.NOCLIENT IS NULL ) THEN
    NEW.NOCLIENT = GEN_ID(TRIG_CLIENT_GenId,1);
END

Ce trigger permet automatiquement de renseigner la clé primaire de la table. Il n'est alors plus nécessaire
d'intervenir, Interbase le fait pour nous. Une telle approche permet d'éviter les erreurs en cas d'omission.
Création d'une table

create table tclient(
 noms char(12),
 prenom char(12),
 motpasse char(12)
 );

SET TERM ^;
CREATE TRIGGER TRIG_CLIENT_GenId FOR CLIENT
ACTIVE AFTER INSERT AS
BEGIN
  IF ( NEW.motpasse IS NULL ) THEN
    NEW.motpasse = 'tmp';
END

La création de cette table ainsi que la procédure stockée permet de mettre un mot de passe si aucun n'a été inclus.Il
est possible de faire appel à une procédure stockée ou un UDF dans un trigger

UDF

UDF signifie fonction définie par l'usager (user defined function). Ces UDF sont bien entendu exécutés sur le serveur, la majorité du temps, le langage utilisé est le c ou le c++. Il est possible d'utiliser d'autre langage. Dans les répertoires d'Interbase, il y a de nombreux exemples.

Les UDF sont utilisés pour ajouter des fonctionnalités non disponibles dans SQL.
Les UDF ont 2 défauts: lorsqu'une UDF est exécuté, aucun accès à la BD ne peut survenir (affecte interbase inférieur à 7). Le second est que si le besoin de changer de machine pour une quelques raisons, on doit installer tous les UDF avant de restaurer la sauvegarde de la bd.

Lorsqu'on créer une UDF, on doit garder à l'esprit qu'elle devrait être le plus simple possible. Il est tout à faire possible dans une UDF d'exécuter un programme ou de faire des insertions, suppression... mais une telle approche peut mettre diminuer significativement les performances du serveur de plus ça pourraient occasionner une congestion des transactions.
Interbase inclus plusieurs UDF dont:
  • lower: converti une chaîne de caractère en minuscule
  • strcat: concatène deux chaînes
  • substr: retourne une portion d'une chaîne
  • trim: enlève les espaces d'une chaîne
  • trunc: trunc une chaîne
  • sysdate: retourne la date du système
  • sqrt: retourne la racine carré d'un double
  • abs : retourne la valeur absolue d'un double

Création d'une UDF avec delphi

Nous allons créer une UDF qui retournera la taxe (7%) sur un chiffre passé en paramètre
  • Choisir l'expert dll
  • Ajouter une unité au projet
  • Sauvegarder l'unité sous le nom MathFct
  • Sauvegarder le projet sous le nom brudflib
L'unité MathFct à l'allure suivante
unit MathFct;

interface

function TaxeTps(var i:Double): Double; cdecl; export;

implementation

function TaxeTps(var i:Double): Double; cdecl; export;
begin
  result := i*0.07+i;
end;

end.

Voici maintenant le code du projet
library brudflib;

uses
  SysUtils,
  Classes,
  MathFct in 'MathFct.pas';

{$R *.res}

exports
  TaxeTps;

begin
end.

Notez la présence du exports. TaxeTps fait partie de cette section, elle pourra donc être utilisé par un autre programme.
Il aurait été possible dans l'unité MathFct d'avoir plusieurs fonctions, mais que seulement quelque unes soit accessible
de l'extérieur.Ne pas oublier que tous les paramètres dans une UDF sont passés par pointeur donc il ne faut pas oublier
de mettre var devant nos paramètres.

En exécutant le projet, une dll est créé brudflib.dll. Cette dll doit être placée dans le répertoire UDF d'Interbase, habituellement c'est: disqu:\Borland\InterBase\UDF\. Il faut par la suite déclarer cette fonction pour l'utiliser dans Interbase.

Syntaxe de la déclaration de l'UDF pour Interbase

Voici la syntaxe d'une UDF:
DEFINE EXTERNAL FUNCTION name [<datatype> | CSTRING (int)
[, <datatype> | CSTRING (int) ...]]
RETURNS {<datatype&gt; [BY VALUE] | CSTRING (int)}
ENTRY_POINT "<entryname>"
MODULE_NAME "<modulename>" ;

Déclarons maintenant la fonction à Interbase
Declare external function f_TaxeTps float
  returns float by value
  entry_point 'TaxeTps' module_name 'brudflib.dll';

Il est maintenant possible d'utiliser cette fonction dans Interbase. Nous pouvons donc effectuer ce genre de
requête:
select f_TaxeTps(12.5) from rdb$database

L'exécution de cette requête retournera bien attendu 13,375. Les fonctions ainsi créées peuvent être ensuite utilisées dans n'importe quelle requête. Imaginons que nous avons une table logicielle avec un champ prix. Nous pourrions connaitre le coût du logiciel après taxe en faisant: select f_TaxeTps(prix) from logiciel.

String

Jusqu'à maintenant, nous avons utilisé que des nombres, nous allons maintenant utilisé des chaînes de caractères. Les strings utilisés par Delphi, lui sont propres. Interbase ne les utilise pas. Nous allons donc être obligés d'utiliser la fonction PChar.

function MixStr(Incoming: PChar):PChar;cdecl; export;
var OutStr, IncomingStr: string;
    i, j: Integer;
begin
  IncomingStr := StrPas(Incoming);
  OutStr := '';
  while Length(IncomingStr) > 0
  do begin
       i := Random(Length(IncomingStr) - 1) + 1;
       OutStr := OutStr + IncomingStr[i];
       Delete(IncomingStr, i, 1);
     end;
  StrPCopy(Incoming, OutStr);
  result := Incoming;
end;

Tappez ce code sous votre outil de gestion de bd:
Declare external function f_MixStr cstring(64)
  returns cstring(64)
  entry_point 'MixStr' module_name 'brudflib2.dll';
select f_MixStr('linux') from rdb$database 
Vous devriez voir que les lettres du mot Linux ont changé de position.

Suppression

Il suffit de taper drop external function nom_de_la_fonction pour la supprimer de la bd. Pour notre fonction, ça serait:

drop external function f_MixStr;