DB2/400


Création et Gestion des triggers

BoTTom

| Triggers

 Définitions : 


 Un TRIGGER est un programme contenant un ensemble d'actions effectuées   automatiquement quand une opération de modification est réalisée sur   une table ou un fichier spécifique.
 L'opération de modification (TRIGGER EVENT) peut être une insertion,   une mise à jour ou une suppression depuis un programme applicatif.
 Le moment d'appel du programme (TRIGGER TIME) peut se situer avant ou   après l'opération de modification du fichier.
 Dans le cas d'une opération de mise à jour, l'appel du programme peut   se faire soit toujours, soit uniquement en cas de modification d'une   valeur (TRIGGER UPDATE CONDITION).


|

 Cas d'utilisation : 

 Les triggers peuvent être employés pour :
     - obliger au respect de règles internes
     - valider des données d'entrée
     - générer une valeur unique liée à un nouveau champ dans un autre         fichier (fonction de substitution)
     - écrire dans un autre fichier pour conserver une trace
     - mettre à jour un fichier de références croisées
     - accéder à des fonctions système (par ex. imprimer un message quand         une règle est violée)
     - dupliquer les données dans un autre fichier pour augmenter         la sécurité (sur un autre système ?)

|

 En pratique : 
 Pour utiliser la fonction trigger, vous devez écrire un programme,   puis l'ajouter à un fichier physique.


                    Ajouter déclencheur fich phys (ADDPFTRG)   
 Indiquez vos choix, puis appuyez sur ENTREE. 
 Fichier physique . . . . . . . . FILE            #########     Bibliothèque . . . . . . . . .                  *LIBL        Moment de déclenchement  . . . . TRGTIME         ########   Evénement de déclenchement . . . TRGEVENT        ########   Programme  . . . . . . . . . . . PGM             ##########     Bibliothèque . . . . . . . . .                  *LIBL        Remplacer déclencheur  . . . . . RPLTRG         *NO  



|

                    Ajouter déclencheur fich phys (ADDPFTRG)   
 Indiquez vos choix, puis appuyez sur ENTREE. 
 Fichier physique . . . . . . . . >  CLIENTP       Nom    Bibliothèque . . . . . . . . .     *LIBL       Nom, *LIBL, *CURLIB  Moment de déclenchement  . . . . >  *BEFORE       *BEFORE, *AFTER  Evénement de déclenchement . . . >  *UPDATE  <--+ *INSERT, *DELETE, *UPDATE  Programme  . . . . . . . . . . . >  TRIGGER1    | Nom    Bibliothèque . . . . . . . . .     *LIBL     | Nom, *LIBL, *CURLIB  Remplacer déclencheur  . . . . .   *NO         | *NO, *YES  Condition déclenchement en màj     *ALWAYS  <--+ *ALWAYS, *CHANGE                                                 |                                                 |                            .....................|........................                            : le mot-clé TRGUPDCND ne s'affiche que si   :                            : TRGEVENT est saisi avec la valeur *UPDATE  :                            :............................................:



|
Visualisation sur la commande DSPFD
 Fichier . . . . :   QPDSPFD                          Page/Ligne  1/1          Contrôle  . . . .                                    Colonnes    1 - 78          Recherche . . . .                                    *...+....1....+....2....+....3....+....4....+....5....+....6....+....7....+...      14/04/95                Description de fichier   Paramètres de la commande DSPFD     Fichier . . . . . . . . . . . . . . . . . . : FILE       CLIENTP       Bibliothèque  . . . . . . . . . . . . . . :            *LIBL     Type d'information  . . . . . . . . . . . . : TYPE       *ALL     Attributs du fichier  . . . . . . . . . . . : FILEATR    *ALL     Système . . . . . . . . . . . . . . . . . . : SYSTEM     *LCL                                                                       ......................................................................  :Description de déclencheur                                          :  :      Heure de déclenchement  . . . . . . . . :            *BEFORE  :  :      Evénement de déclenchement. . . . . . . :            *UPDATE  :  :      Condition de déclenchement. . . . . . . :            *ALWAYS  :  :      Nom du programme  . . . . . . . . . . . : PGM        TRIGGER1 :  :        Bibliothèque  . . . . . . . . . . . . : LIB        AF4TEMP  :  :....................................................................:

Mais aussi avec Sysytem i Navigator
|

                     Enlever déclencheur fichier (RMVPFTRG)    
 Indiquez vos choix, puis appuyez sur ENTREE. 
 Fichier physique . . . . . . . . FILE            ##########     Bibliothèque . . . . . . . . .                  *LIBL        Moment de déclenchement  . . . . TRGTIME        *ALL       Evénement de déclenchement . . . TRGEVENT       *ALL     
                          ............................................                           : Les triggers peuvent être tous enlevés   :                           : en une seule fois, ou bien moment par    :                           : moment, événement par événement.         :                           :..........................................:






| V5R10 : désactivation des triggers


 la gestion des triggers est  modifiée en V5.10 :
 -un trigger peut être suspendu
 -on peut associer 300 triggers à un fichier
 -un trigger possède un nom long et une bibliothèque
 -on peut associer un trigger à la lecture (peut-elle avoir lieu ?)
 -l'écriture des triggers peut se faire en SQL PSM (L4G)
    dans ce dernier cas, - le trigger peut-être associé à la modification                             d'un champ.
                         - vous pouvez indiquer des conditions au lancement                             du trigger.
                         - il peut-être lancé à chaque ligne modifiée ou bien                              une fois par instruction (UPDATE par ex.).

|

 un trigger s'identifie maintenant par un nom long et une bibliothèque   de trigger (ce qui n'est pas la bibliothèque du programme)
 voyons la commande ADDPFTRG :
                    Ajouter déclencheur fich phys (ADDPFTRG) 
 Fichier physique . . . . . . . .                 Nom    Bibliothèque . . . . . . . . .     *LIBL       Nom, *LIBL, *CURLIB  Moment de déclenchement  . . . .                 *BEFORE, *AFTER  Evénement de déclenchement . . .                 *INSERT, *DELETE, *UPDATE.  Programme  . . . . . . . . . . .                 Nom    Bibliothèque . . . . . . . . .     *LIBL       Nom, *LIBL, *CURLIB  Remplacer déclencheur  . . . . .   *NO           *NO, *YES  Déclencheur  . . . . . . . . . .   *GEN                                                                                                                                                                                                                                                                                                                                                                                                                                        Bibliothèque du déclencheur  . .   *FILE         Nom, *FILE, *CURLIB

|

 le paramètre Déclencheur (TRG) permet d'indiquer un nom de trigger, ce   dernier ne pouvant plus être identifié par le couple moment/évenement.
  le nom ne doit pas dépasser 128 c. (ou 258 entre guillemets)
  les triggers existants en V4 sont nommés
     QSYS_TRIG_nombib_nomfichier_00001
     les noms de bibliothèque et de fichier étant complémentés à 10 par "_"
 le paramètre Bibliothèque (TRGLIB) indique la bibliothèque du trigger   (et non la bibliothèque du pgm lui même qui est fournie dans PGM)
  la valeur *FILE attribue le nom de la bibliothèque du fichier,    *CURLIB, la bibliothèque en cours.

 c'est le couple  TRGLIB/TRG qui identifie un trigger de manière unique.   (ATTENTION, l'unicité se faisant sur tout le système)

|

 désactivation d'un trigger :
 CHGPFTRG  FILE(QGPL/TEST) TRG(TRG_TEST_READ) TRGLIB(*FILE)
           STATE(*ENABLED | *DISABLED)
 à l'état *DISABLED le trigger n'est plus appelé lors des E/S.
 la commande DSPFD indique l'état actuel
............................................................................ :Description de déclencheur                                                : :                                                                          : : Nom de déclencheur. . . . . . . . . . . . . : TRG        QSYS_TRIG_BDVIN_: :       ____MA_CAVE____000001                                              : :   Bibliothèque de déclencheur . . . . . . . :            BDVIN9          : :   Etat du déclencheur . . . . . . . . . . . : STATE      *ENABLED        : :   Etat du déclencheur . . . . . . . . . . . :            *OPERATIVE      : :..........................................................................:
 *operative indique un trigger associé à un fichier avec au moins un membre.


|

 Programme trigger et contrôle de validation : 
            FONCTIONNEMENT HORS CONTROLE DE VALIDATION 
 Si le programme applicatif et le programme trigger ne s'exécutent pas   sous contrôle de validation, toute erreur du programme trigger laissera   les fichiers dans l'état au moment de l'erreur.

            FONCTIONNEMENT SOUS CONTROLE DE VALIDATION 
 Si le programme applicatif seul s'exécute sous contrôle de validation,   l'opération COMMIT du programme applicatif s'applique uniquement à ses   propres modifications.

 Si le programme trigger seul s'exécute sous contrôle de validation, les   modifications effectuées par le programme trigger sont validées quand :
      - le groupe d'activation se termine (COMMIT implicite)       - une opération de COMMIT est demandée par le programme

|
 Si le programme applicatif et le programme trigger s'exécutent sous le
  même contrôle de validation, un arrêt anormal du programme trigger
  entraînera le Rollback de toutes les opérations qui lui sont associées.

 Un Rollback est également effectué sur l'opération de modification   d'origine. Ceci nécessite que le programme trigger envoie un message   d'exception quand l'erreur est détectée.
 Les deux programmes sont alors fortement couplés  , c'est la solution pour   un trigger chargé de détecter des erreurs pouvant annuler l'action B de D.

 Si le programme applicatif et le programme trigger s'exécutent sous des   contrôles de validation différents, l'opération COMMIT du programme   applicatif n'agit que sur son propre contrôle de validation.
 La validation des modifications du programme trigger doit être effectuée   de façon distincte.
 les deux programmes sont faiblement couplés, c'est la solution pour un   trigger chargé de répercuté des actions (dans un autre fichier)

| écriture d'un trigger

 Ecriture d'un programme trigger : 

 Une opération de modification, sur un fichier physique ayant un trigger   associé, déclenchera l'appel du programme trigger concerné.
 L'opération de modification passera 2 paramètres au programme trigger.  Dans ces paramètres, le programme récupérera une copie des enregistrements   avant et après l'opération.
                      Paramètres                             Types 
     Buffer contenant les informations sur l'opération      Char(*)      Longueur du buffer                                     Binary(4)
 Le buffer contient 2 parties, une partie fixe et une partie variable.
 La partie fixe contient le nom du fichier, du membre, l'événement, le   moment, le niveau de verrouillage de la définition de validation, et le   CCSID de l'enregistrement concerné par l'opération (soit 95 c.).
 La partie variable contient les images de l'enregistrement avant et   après modification.

|

 Description du buffer : 
   Dépl   Type              Champ 
     0   Char(10)    Nom du fichier physique     10   Char(10)    Nom de la librairie     20   Char(10)    Nom du membre     30   Char(1)     Evénement déclencheur   (1=INSERT,2=DELETE,3=UPDATE)     31   Char(1)     Moment du déclenchement (1=AFTER,2=BEFORE)     32   Char(1)     Niveau de verrouillage de la définition de validation                                              (0=*NONE,1=*CHG,2=*CS,3=*ALL)     36   Binary(4)   CCSID des données     48   Binary(4)   Image avant : Déplacement de l'enregistrement     52   Binary(4)                 Longueur de l'enregistrement     56   Binary(4)                 Déplacement de la description des champs     60   Binary(4)                 Longueur de la description des champs     64   Binary(4)   Image après : Déplacement de l'enregistrement     68   Binary(4)                 Longueur de l'enregistrement     72   Binary(4)                 Déplacement de la description des champs     76   Binary(4)                 Longueur de la description des champs     96   Char(*)     Début de la zone variable

|

 Le pgm trigger peut signaler une erreur logique :
    + il doit envoyer un message à l'appelant
      attention avec ILE, une procédure PEP est ajoutée dans la liste        d'invocation.
      PEP = Program Entry Point
        il faut donc envoyer 2 niveaux au-dessus
        ou utiliser la valeur *PGMBDY avec l'API QMHSNDPM (envoi de msg)

    + l'action base de données est interrompue inachevée.

    + le message CPF502B est émis, le message envoyé par votre trigger         n'est pas retransmis (donc le texte est inutilisable)


|
 Attention aux cas suivants, provoquant une erreur :

      - mise à jour du même enregistrement déjà modifié par l'opération de         modification, ou par une opération dans le programme trigger.
      - génération d'un conflit sur le même enregistrement à l'intérieur         d'une opération de modification. Par exemple, un enregistrement         inséré par l'opération de modification puis supprimé par le         programme trigger.
 Lors de l'ajout d'un programme trigger à un fichier physique, ce dernier   et tous les logiques associés sont verrouillés de façon exclusive. 
 Le programme s'applique à tous les membres du fichier physique.
 Toute modification effectuée par le biais d'un fichier logique, ou d'une   vue SQL, entraîne l'appel du programme trigger.
 Une commande CPYF CRTFILE(*YES) ne reporte pas les attributs triggers sur   le fichier créé.
 Une commande CRTDUPOBJ reporte tous les attributs triggers. 

| V3R70: ALWREPCHG


             la gestion des triggers change en V3R70
 la commande ADDPFTRG accepte un nouveau paramètre ALWREPCHG.
 lorsque ce paramètre est renseigné à oui, lors d'un ajout ou d'une   mise à jour, l'enregistrement ajouté ou remplacé est celui du buffer   du trigger (modifié par celui-ci) et non l'original.
 RAPPEL, un trigger reçoit un buffer contenant :
 - des informations système
 - la version AVANT de l'enregistrement
 - la version APRES.
 le trigger peut donc maintenant faire de la substitution de données    et remplacer certaines informations renseignées par le programme.
 ce sont les données du trigger qui seront placées dans la base et non    celles initialement indiquées par le programme.

|

 Exemple :
 soit un fichier article avec le buffer suivant
      | codearticle |  libellé                   |  datecrt   |       |             |                            |            | 
 le trigger reçoit un paramètre
   infos système              | code |libellé |date | code |libellé |date |  |              | Dep1 | Dep2 |                     |                     |                     !     !    ^                     ^                    !-----!----!                     !                          !                          !                          !--------------------------!

 dep1 indique le déplacement pour aller à la version avant du buffer  dep2  "       "    "          "   "           "     après du buffer


|

 le paramètre ALWREPCHG fait que si le trigger modifie la version après    c'est cette version de l'enregistrement qui est stockée dans la base.
 Imaginons maintenant de rajouter dans le fichier article une zone SIECLE   complétant la zone date de création (6,0)
   (repenser à la nouveauté CHGPF qui permet de modifier un fichier ONLINE)
   Puis recompilons tous les programmes utilisant le fichier article.

 si nous n'avons pas modifié le source de ces programmes la zone SIECLE    n'est JAMAIS renseignée.
 Ajoutons maintenant un trigger BEFORE/UPDATE, BEFORE/INSERT qui calcule   automatiquement le SIECLE en fonction de l'année.

 Vous avez le SIECLE  dans vos fichiers,   reste à modifier les programmes devant afficher cette information.

|

techniquement cela ce fait de la manière suivante :
définition des paramètres reçus :
     Dparam            ds      D  filler                       64      D   dep2                        10I 0
puis déclaration d'une DS externe(avec le fichier article) basée sur un   pointeur.
il s'agit en fait d'un découpage théorique qui n'a pas d'existence tant que le pointeur n'a pas de valeur.
     Dptr              S               *      Darticle        e ds                  based(ptr) EXTNAME(articlp1)
 attribuer à ptr l'adresse de param revient à "plaquer" article sur param,   "+ dep2" revient à le décaler de "dep2" octets.
     c                   eval      ptr = %addr(param) + dep2

|
code complet

     Dparam            ds      D  filler                       64      D   dep2                        10I 0      Dptr              S               *      Darticle        e ds                  based(ptr) EXTNAME(articlp1)
     C     *entry        plist      C                   parm                    param      C                   parm                    lgparam
     c                   eval      ptr = %addr(param) + dep2      c                   if        datcrt < 500101      c                   eval      siecle = 20      c                   else      c                   eval      siecle = 19      c                   endif
      * n'oublions pas le      C                   return

|

 exemple plus complet avec  1/ gestion de la valeur nulle                             3/ renseignement de la date de création                             2/ test du prix et envoi de l'erreur si < à 0.

     A          R DB2NULLF      A            CODART         6      A            LIBART        25          ALWNULL      A            DATCRT          L         DATFMT(*EUR)      A            PRIX           7  2       EDTCDE(4)
 en ce qui concerne les valeurs nulles, la fonction %NULLIND() n'est pas   admise car il ne s'agit pas d'un fichier déclaré (au sens ou il n'y a   pas de spécif F dans votre source)

 le système génère donc un variable de travail avec un octet par zone   dans le fichier. (supportant la val. nulle ou pas)
 chaque octet contient '0' si la variable est significative                        '1' si la variable est nulle.

|
      *=========== définition des paramètres reçus =========================
     Dptr              s               *
     Ddata             DS                  based(ptr)
     D  fichier                      10
     D  biblio                       10
     D  membre                       10
     D  evenem                        1
     D  moment                        1
     D  niv_verrou                    1
     D    filler1                     3
     D  ccsid                         9B 0
     D    filler2                     8
      * enregistrement avant, déplacement et longueur
     D  dep_enrav                     9B 0
     D  lg_enrav                      9B 0
      * description des zones nulles avant (déplacement et longueur)
      * il y a un octet par zone dans l'enregistrement
      * contenant 0 (la zone est significative)
      *           1 (elle est nulle)
     D  dep_desav                     9B 0
     D  lg_desav                      9B 0


|
      * enregistrement après (idem)
     D  dep_enrap                     9B 0
     D  lg_enrap                      9B 0
      * zones nulles
     D  dep_desap                     9B 0
     D  lg_desap                      9B 0
     D    filler3                    16
     D  variable                   9900A
     Dparam            S           9999
     Dlgparam          S              9B 0
     Ddep              S              9B 0
     Dprtap            s               *
     Denregap        E DS                  EXTNAME(db2nullpf) PREFIX(ap)
     D                                     based(ptrap)
     dprtnap           s               *
     dnullap           ds                  based(ptrnap)
     D  codart_nap                    1
     D  libart_nap                    1
     D  datcrt_nap                    1
     D  prix_nap                      1



|
      *=========== variables pour API envoi de messages ===================
     D ERRDS           DS
     D  LGDS                   1      4B 0 INZ(16)
     D  LGERR                  5      8B 0
     D  MSGID                  9     15
     D  RESERV                16     16
     D MSGFL           DS
     D  MSGF                         10    INZ('QCPFMSG')
     D  MSGL                         10    INZ('*LIBL')
     D ID              S              7    INZ('CPF9898')
     D MSGTXT          S             50    INZ('prix invalide (doit être >0)')
     D LENTXT          S              9B 0 INZ(50)
     D MSGTYP          S             10
     D PGMQ            S             10    INZ('*PGMBDY')
     D STACK           S              9B 0
     D KEY             S              9B 0

      *=========== DEBUT DU PROGRAMME ======================================      C     *entry        plist      C                   parm                    param      C                   parm                    lgparam       *

|
      * mise en place du paramètre reçu
     C                   eval      ptr = %addr(param)
      *
      * mise en place du buffer après
      *
     C                   if        dep_enrap <> 0
     C                   eval      ptrap = ptr + dep_enrap
     C                   endif
     C                   if        dep_desap <> 0
     C                   eval      ptrnap = ptr + dep_desap
     C                   endif
      * si libart est à blanc alors ==> valeur nulle
     c                   if        aplibart = *blank
     c                   eval       libart_nap = '1'
     c                   endif
      * vérification du prix (doit être positif)
     C                   if        apprix <= 0
     C                   exsr      env_msg
     C                   endif
      * mettre date du jour dans DATCRT
     c                   time                    datcrt
     C                   return

|
      *
     C     env_msg       begsr
      *
      * envoi d'un message diagnostic au pgm applicatif
      *
      * pour expliquer les raisons de l'erreur (cela laisse une trace)
      *
      *
     C                   CALL      'QMHSNDPM'
     C                   PARM                    ID
     C                   PARM                    MSGFL
     C                   PARM                    MSGTXT
     C                   PARM                    LENTXT
      * type *diag
     C                   PARM      '*DIAG'       MSGTYP
     C                   PARM                    PGMQ
      * deux niveaux au dessus (au pgm applicatif)
     C                   PARM      2             STACK
     C                   PARM                    KEY
     C                   PARM                    ERRDS



|
      *
      * envoi d'un message escape( erreur) au système
      *
      *  c'est lui qui annule l'action base de données
      *
     C                   CALL      'QMHSNDPM'
     C                   PARM                    ID
     C                   PARM                    MSGFL
     C                   PARM                    MSGTXT
     C                   PARM                    LENTXT
      * *escape pour arrêter le traitement
     C                   PARM      '*ESCAPE'     MSGTYP
     C                   PARM                    PGMQ
      * un niveau au dessus ==> DB2
     C                   PARM      1             STACK
     C                   PARM                    KEY
     C                   PARM                    ERRDS
     C                   endsr


| Triggers à la lecture


 un trigger peut-être associé à l'ordre de lecture :  (trigger système [non SQL] uniquement.)
 ADDPFTRG ...   TRGTIME(*AFTER) TRGEVENT(*READ) ALWREPCHG(*NO)
  on intercepte la lecture après, uniquement.    mais c'est le buffer avant qu'il faut traiter (???)
  les événements sont codés :  1 = insert  2 = delete                                3 = update  4 = read   ........................................................................   : IL FAUT IMPERATIVEMENT :                                             :   :    - utiliser ces codes, les déplacements sont TOUJOURS renseignés   :   :    - utiliser les déplacements (offsets), la position des données    :   :       a été modifiée sur cette version.                              :   :......................................................................:
   on ne peut pas substituer des données dans le buffer [ALWREPCHG(*NO).]
  on peut empêcher la lecture, par l'envoi d'un message d'erreur.


| Triggers en SQL PSM (PL/SQL)


 et enfin la création de triggers en SQL (V5R10) :
                                                    --- BEFORE----  CREATE TRIGGER --nom-trigger----------------------!              !------>                                                     ---AFTER------
       ---INSERT-----------------------------------        !                                          !  >-----!--DELETE----------------------------------!-----REFERENCING--->        !                                          !        !--UPDATE----------------------------------!                 !                      !                 !-- OF colonne --------!
      ----OLD ROW -- AS ---nom-correlation-------       !                                         !       !---NEW ROW -- AS ---nom correlation------!   -FOR EACH STATEMENT-  >----!                                         !---!                   !->       !---OLD TABLE -- AS --identifiant---------!   --- FOR EACH ROW----       !                                         !       !---NEW TABLE -- AS --identifiant---------!

|


                                           --MODE DB2ROW--- >---- SET OPTION (option de compilation)--!                !-------->                                            --MODE DB2SQL---
>-----WHEN (condition d'exécution du trigger)---------------->
>-BEGIN   ... (corps du trigger )   ...   END
quelques explications :
 + BEFORE ne peut pas contenir des ordres INSERT, UPDATE, DELETE *           mais peut modifier les variables de la table par SET.
   AFTER peut contenir des ordres INSERT, UPDATE, DELETE sur d'autres tables
* Depuis la 7.1, nous pouvons ajouter l'option SQL_MODIFIES_SQL_DATA à QAQQINI
*DEFAULT identique à *NO
*NO Un trigger BEFORE ne peux pas modidier des tables
*YES Un trigger BEFORE peux modififer des tables


|

 + INSERT | DELETE | UPDATE représentent l'événement associé(READ non admis)
   UPDATE OF "colonne" permet de ne déclencher le trigger que si "colonne"      a été modifiée.
 + REFERENCING OLD ROW | NEW ROW
               OLD n'est valide qu'avec UPDATE, DELETE                NEW   "     "       "    UPDATE, INSERT

    exemple :  REFERENCING OLD ROW AS avant NEW ROW AS apres
                puis dans le code :
                  IF apres.prix > avant.prix ....

  OLD ROW, NEW ROW ne peuvent pas être utilisés avec l'option     FOR EACH STATEMENT.

|

exemple, lors d'une modification si le prix actuel est < au prix d'achat   il doit prendre au moins la valeur du nouveau prix d'achat :
 ........................................................................  :                                                                      :  : CREATE TRIGGER  MAJ_PRIX BEFORE UPDATE OF CAV_PRXACTUEL              :  :                           ON BDVIN/MA_CAVE                           :  :  REFERENCING NEW ROW AS NOUVEAU                                      :  :  FOR EACH ROW MODE DB2ROW                                            :  :                                                                      :  : BEGIN                                                                :  :                                                                      :  :  IF NOUVEAU.CAV_PRXACTUEL < NOUVEAU.CAV_PRIX THEN                    :  :      SET NOUVEAU.CAV_PRXACTUEL = NOUVEAU.CAV_PRIX ;                  :  :  END IF;                                                             :  :                                                                      :  : END                                                                  :  :                                                                      :  :......................................................................:


|


 + REFERENCING OLD TABLE | NEW TABLE
                OLD n'est valide qu'avec UPDATE, DELETE                 NEW   "     "       "    UPDATE, INSERT

     OLD table représente une table temporaire contenant l'ensemble          des enregistrements modifiés ou supprimés.          (les valeurs sont celles avant l'instruction d'origine)
     NEW table représente une table temporaire contenant l'ensemble          des enregistrements insérés ou supprimés.          (les valeurs sont celles après l'instruction d'origine et après            l'appel du(des) trigger(s) BEFORE)
    ces deux options ne peuvent être utilisées qu'avec :       le mode DB2SQL, et le moment AFTER.       (FOR EACH ROW ou FOR EACH STATEMENT)


|
exemple, on trace le cumul des prix modifiés :
.......................................................................
: CREATE TRIGGER  VIN_CUMUL  AFTER UPDATE OF CAV_PRIX                 :
:                            ON BDVIN9/MA_CAVE                        :
: REFERENCING OLD TABLE AS OCAVE                                      :
:           NEW TABLE AS NCAVE                                        :
: FOR EACH STATEMENT MODE DB2SQL                                      :
:                                                                     :
:  BEGIN                                                              :
:   DECLARE AV_CUMUL DEC(11, 2);                                      :
:   DECLARE AP_CUMUL DEC(11, 2);                                      :
:                                                                     :
:   SELECT SUM(CAV_PRIX) INTO AV_CUMUL FROM OCAVE  ;                  :
:   SELECT SUM(CAV_PRIX) INTO AP_CUMUL FROM NCAVE  ;                  :
:                                                                     :
:   INSERT INTO AF4TEST/CUMUL VALUES(NOW() , AV_CUMUL, AP_CUMUL) ;    :
:  END                                                                :
:.....................................................................:
 suite à un ordre : update bdvin9/ma_cave set cav_prix = cav_prix * 1,5

  QUAND                          AVANT_CUMUL      APRES_CUMUL     2001-08-23-14.25.20.181728          205,95           308,92

|

 + FOR EACH ROW | EACH STATEMENT
   FOR EACH ROW le trigger est appelé une fois par ligne
       cas des contrôles,  mises à jour de champs, traces ...

   FOR EACH STATEMENT le trigger est appelé une fois par instruction.
       cas de recalcule automatique de cumuls, etc...
     ATTENTION : le trigger est appelé même si aucune ligne n'est impactée
 + MODE DB2ROW , DB2SQL
   MODE DB2ROW, l'appel a lieu à chaque ligne modifiée,
                BEFORE ET AFTER sont valides,
                FOR EACH STATEMENT et l'utilisation de OLD TABLE/NEW TABLE                   ne sont PAS admis.

|

   MODE DB2SQL, l'appel a lieu à la fin de l'instruction,
                seul AFTER est valide,
                FOR EACH STATEMENT et l'utilisation de OLD TABLE/NEW TABLE                   SONT admis.
   les valeurs reçues sont bien celles utilisée lors de la transaction,     la grande différence est que le trigger n'est appellé que si l'ordre     SQL va jusqu'au bout.
 exemple : 
   mise à jour d'un fichier dont la dernière ligne est verrouillée par un     autre JOB, le trigger écrit dans une table afin de conserver une trace.
   avec MODE DB2ROW le trigger est appellé (n - 1 ) fois,  n étant le     nombre d'enregistrements à modifier.
   avec MODE DB2SQL le trigger n'est pas activé (fichier trace vide).

|

 + SET OPTIONS
       options du compilateur (C), particulièrement DBGVIEW = *SOURCE
 + WHEN (condition)
       condition quant à l'appel du trigger
 exemple :  ........................................................................  :                                                                      :  : CREATE TRIGGER  MAJ_PRIX BEFORE UPDATE OF CAV_PRXACTUEL              :  :                           ON BDVIN/MA_CAVE                           :  :  REFERENCING NEW ROW AS NOUVEAU                                      :  :  FOR EACH ROW MODE DB2ROW                                            :  :  WHEN (NOUVEAU.CAV_PRXACTUEL < NOUVEAU.CAV_PRIX)                     :  :                                                                      :  : BEGIN                                                                :  :      SET NOUVEAU.CAV_PRXACTUEL = NOUVEAU.CAV_PRIX ;                  :  : END                                                                  :  :......................................................................:

|

 corps du triggers, (voir les procédures cataloguées)
 BEGIN (ATOMIC | NON ATOMIC)
  déclaration de variables                          ;   déclaration de curseurs                           ;   déclaration de handler (gestion des exceptions)   ;

 instructions SQL (INSERT, UPDATE, DELETE, FETCH)   ;
 instructions de structuration
                  (IF, WHILE, LOOP)                 ;
 assignation de variables (SET ou VALUES)           ;
 génération d'une exception (SIGNAL , RESIGNAL)     ;

 END

|

 Quelques nouveautés en ce qui conserne la gestion des exceptions :
 rappel, une erreur se prévoit par
    DECLARE nom_condition CONDITION FOR SQLSTATE 'n°'
 et/ou                                                  --SQLSTATE 'n°'--               ---CONTINUE-------                       !- nom-condition--!     DECLARE  !------------EXIT--!-- HANDLER FOR -------!- SQLEXCEPTION---!->               -- UNDO-----------                       !- SQL WARNING--- !                                                         - NOT FOUND -----
>-- instruction(s) SQL à exécuter en cas d'erreur-- ;

 avec les triggers :
   UNDO n'est pas valide, utilisez ATOMIC (trigger complet ou rien)      ou bien gérer le COMMIT/ROLLBACK


|

 GET DIAGNOSTIC variable ROW COUNT : retourne le nombre de lignes affectées   "     "         "      RETURN STATUS "       "  dernier code status

 pour signaler un code retour, terminer la procédure par RETURN n°
       RETURN 0    ou        RETURN -200

 pour signaler une erreur, utilisez SIGNAL (ordre de la V4)
   SIGNAL SQLSTATE 'n°'---------------------------------------- -                          !--- SET MESSAGE_TEXT 'votre-texte'--!
   RESIGNAL  doit être placé dans un condition handler
   (sans paramètres, il génère l'erreur qu'il a lui-même reçu.)


|
SQLSTATE est amené à remplacer SQLCOD/SQLCODE (orientation ISO)

 SQLSTATE est composé de CINQ caractères (chiffres ou lettres)
 les deux premiers caractères représentent la classe,   les codes commençant par 0 à 6 et A à H sont réservés
  00 : Success (terminé normalement)   01 : Warning (message d'attention), donne SQLCOD = +438)   02 : No DATA (l'équivalent de SQLCOD = 100)   03 à ZZ : erreur, donne SQLCOD = -438.    (38 signale la fin anormale, d'une fonction par exemple)
  le troisième caractère représente la sous classe.   les sous classes de '0' à 'H' sont réservés, I à Z est libre.
 les codes commençant par 7,8,9 et I à Z sont libres,les sous classes aussi.  ce qui fait que vous devez utiliser :  - '00000' quand tout va bien.  - '01Hxx' pour signaler un WARNING.  - '38Ixx' à '38Zxx' pour signaler vos erreurs dans vos fonctions/triggers.  - un code disponible dans les autres cas.

|

Sinon, voici un rappel succinct des ordres de structuration du SQL PSM :
IF test  THEN  instruction ELSEIF instruction ELSE instruction END IF;
CASE WHEN ....WHEN ... ELSE ...  END CASE;   (comme dans le SELECT)
LEAVE label;   sortie de la boucle qui s'appelle "label"
GOTO  label;   aller à l'instruction qui s'appelle "label"

label: LOOP instructions END LOOP;
label: WHILE (condition) instructions END WHILE;
label: REPEAT instructions UNTIL (condition) END REPEAT;
FOR  nom AS C1 CURSOR FOR select .. from .. where ..  instructions END FOR;

Tout cela pouvant être réalisé depuis iSeries Navigator

Puis propriétés


Top


 

 


 

© AF400 - Volubis