Procédures cataloguées et fonctions

BoTTom |    Changer de couleur
 
   Procédures cataloguées [STORED PROCEDURES] :
  -----------------------------------------------
 
 SQL permet maintenant de demander l'exécution d'un programme sur un serveur
  distant (normalement devant réaliser une action sur la base de ce serveur)
 
 Cette fonction n'est pas encore normalisée (mais largement répandue).
 
  - certains SGBD n'autorisent que des procédures SQL.
 
  - DB2/400 autorise l'appel à n'importe quel programme.
 
    + avec passage de paramètres.
 
    + ces paramètres pouvant être renseignés par le pgm distant.
 
 
 Syntaxe  CALL nom-procédure----------------------------------------------
                                 |                       |
                                 |-(param1,[param2,...])-|
 


|    Changer de couleur
 
 nom-procédure = nom du pgm à appeler ou procédure telle que définie par
                  DECLARE PROCEDURE (voir ci-dessous)
 
                 la procédure est recherchée dans la collection en cours
 
                  pour qualifier :  BIB/PGM avec la convention système
                                    BIB.PGM avec la convention SQL
 
                                      voir: STRSQL    paramètre NAMING
                                            CRTSQLxxx paramètre OPTION
 
 paramx        = paramètre envoyé (ou reçu) qui peut être
 
                 + une constante
                 + une variable du pgm (:var)
                   (par défaut :var est en entrée/sortie avec SQL statique
                                    en entrée (envoyée) avec SQL dynamique)
                 + NULL
                 + une valeur spéciale (CURRENT DATE/TIME/TIMEZONE , SERVER)
                                       (USER)
 


|    Changer de couleur
 
Il est possible de déclarer une procédure afin de préciser certaines options
 
DECLARE -nom-proc-PROCEDURE------------------------------------------------>
                             |        |-IN----|                         |
                             |-(param---OUT-----type de données(lg)-----|
                                      |-INOUT-|
   |-EXTERNAL------------------|
 >------------------------------LANGUAGE--langage---------------------------
   |-EXTERNAL NAME-nom-externe-|                   |-SIMPLE CALL-----------|
                                                   |-SIMPLE CALL WITH NULL-|
 
 définition des paramètres :
 
    IN  le paramètre est uniquement reçu par la procédure
    OUT le paramètre est renvoyé par la procédure
    INOUT le paramètre est utilisé dans les deux sens
 
    type de donnée(lg) : idem CREATE TABLE    CHAR(xx)
                                              INT(lg, dec)
                                              etc ...
 


|    Changer de couleur
 EXTERNAL NAME permet de définir le nom réel du pgm sur le serveur, si cette
  clause n'est pas renseignée c'est le nom de procédure qui est utilisé.
 
 LANGUAGE permet de définir le langage d'origine du pgm à exécuter
 
          C, CL, COBOL, COBOLLE, RPG, RPGLE, REXX, ...
 
          ceci est obligatoire avec une procédure REXX
 
          si cette clause n'est pas utilisé le server recherche l'attribut
           du pgm et par défaut le considère comme un pgm C.
 
 
 SIMPLE CALL WITH NULL signifie que le pgm doit recevoir les indicateurs
                        indiquant les valeurs nulles (254 param maxi)
 
 SIMPLE CALL           le pgm ne reçoit pas ces indicateurs (255 param maxi)
 
 
 l'instruction DECLARE PROCEDURE doit être utilisée pour appeler des
  procédures REXX, et doit être écrite AVANT l'instruction CALL.
 


|    Changer de couleur
 
 
  Particularité de SQL : il ne reconnait pas les membres.
 
     vous pouvez quand même outrepasser en utilisant les procédures :
 
     EXEC SQL
      CALL QSYS.QCMDEXC ('OVRDBF FIC MBR(xxx) OVRSCOPE(*JOB)',
                               0000000035,00000)
     END-EXEC.
 
 
     Attention
 
     le point (dans QSYS.CMDEXC) doit être le qualifiant en cours
     la virgule (dans 0000000035,00000) doit être la marque décimale.
 
  Cette solution peut-être utilisée aussi avec ODBC/JDBC.
 
 
 
 


|    Changer de couleur
 
 Vous pouvez même renvoyer un groupe d'enregistrements en une seule fois :
 
  Dans la procédure appelée, ajoutez :
 
 
       SET RESULT SETS -----------------------------------------------------
                        !                                       !
                        !-----CURSOR nomcurseur-----------------!
                        !                                       !
                        !                                       !
                        !--ARRAY nomdetableau -----FOR x ROWS----
 
 
  Le curseur indiqué (il doit être ouvert) ou l'ensemble des enregistrements
   placés dans le tableau (ou la table en COBOL) représentent les valeurs
   en retour retournées au micro.
 
 
  Cet ordre n'est pas valide en RPG, il n'y a pas d'invite SQL interactif.
 
 


|    Changer de couleur
 
 Vous pouvez enfin préparer tout cela à l'avance en déclarant une fois pour
  toute la procédure et ses particularités ( ce qui est conseillé )
 
 
 
  CREATE PROCEDURE   --------(même syntaxe que DECLARE -------)---->
 
                     ----------------------------------------------.
                       !                                    !
                       !--DYNAMIC RESULT SETS---n-----------!
 
 
    n définit le nombre de "result sets" retournés
 
 
 
   La définition est enregistrée dans les fichiers SYSPROCS et SYSPARMS
    du catalogue.
 
 
 


|    Changer de couleur
 
Exemple (retourne *LIBL)
 
 
1/ enregistrement dans las catalogues
 
CREATE PROCEDURE AF4TOOL/DSPLIBL LANGUAGE RPGLE NOT DETERMINISTIC
CONTAINS SQL EXTERNAL NAME AF4TOOL/DSPLIBL PARAMETER STYLE GENERAL ;
 
 
2/ le pgm (ici le RPG principal)
 
     DDSlibl           ds                  occurs(265)
     D   unebib                      10
     DUSRlibl          ds
     d tblibl                        11    dim(265)
 
     D i               S              5I 0
     D R               S              5I 0
 
     Drtvlibl          PR                  EXTPROC('DSPLIBLC')
     D                             2750


|    Changer de couleur
 
      /free
       rtvlibl(USRLIBL);
 
       for i=1 to 265;
         if tblibl(i) = *blanks and i > 15; // on est dans USRLIBL
            leave;
         else;
            if tblibl(i) <> *blanks;
                R = R + 1;
                %occur(DSlibl) = R;
                unebib = tblibl(i);
            endif;
         endif;
        endfor;
      /end-free
     C/EXEC SQL
     C+ SET RESULT SETS  ARRAY :DSLIBL FOR :R ROWS
     C/END-EXEC
      /free
        return;
      /end-free


|    Changer de couleur
 
 et enfin le CL, utilisant RTVJOBA (ce CL est linké avec le RPG)
 
        PGM        PARM(&LIBL)
         DCL        VAR(&LIBL) TYPE(*CHAR) LEN(2915)
         DCL        VAR(&USRLIBL) TYPE(*CHAR) LEN(2750)
         DCL        VAR(&SYSLIBL) TYPE(*CHAR) LEN(165)
 
             RTVJOBA    USRLIBL(&USRLIBL) SYSLIBL(&SYSLIBL)
             CHGVAR     VAR(&LIBL) VALUE(&SYSLIBL *CAT &USRLIBL)
 
        ENDPGM
 
 
 pour utiliser cette procédure, lancez comme ordre SQL :
 
 
   "CALL DSPLIBL"
 
 
  et traitez (suivant le langage) comme si vous aviez demandé un SELECT
 


|    Changer de couleur
 
 Il est possible de créer des procédures cataloguées SQL en SQL PSM
 
    (même langage que les bases Oracle avec PL/SQL)
 
 ATTENTION :
 
    cela va créer un programme OS/400 qui sera référencé dans les
         catalogues SQL (SYSPROCS et SYSPARMS dans QSYS2)
 
    mais pour créer ce programme OS, SQL va générer un source C contenant
         du SQL et appeller le compilateur.
 
 
    il faut donc le compilateur C sur votre machine jusqu'à la V4R50.
 
 
 
 (la version 4.4 de l'OS va ammener les fonctions, qui pourrons elles
   aussi être écrites en SQL ==> mêmes contraintes, jusqu'en V4r50)
 
 


|    Changer de couleur
 
Exemple général :
 
  CREATE PROCEDURE xxxxxxx/P1
         (in  nouveau_module CHAR(10) ,
          out probleme       integer  )
         LANGUAGE SQL
 
         BEGIN ATOMIC
              DECLARE UNDO HANDLER FOR SQLEXCEPTION
                          SET probleme = -1;
 
              FOR v1 as C1 cursor for SELECT module, nomcours
                                       FROM COURS
                  DO
                  IF nomcours <> ' '
                     THEN
                     UPDATE COURS SET module = nouveau_module
                     WHERE CURRENT OF C1 ;
                  END IF ;
              END FOR;
         END


|    Changer de couleur
 
 I/ syntaxe générale 
 
          --ATOMIC------
 BEGIN ---              ------------------>
          --NOT ATOMIC--
 
     déclaratives
       .../...
 
 
     instructions SQL
       .../...
       .../...
 
 END
 
 ATOMIC ==>   l'ensemble de la procédure doit être réalisé comme un tout,
               vous ne pouvez pas placer d'ordres COMMIT/ROLLBACK
 
 NOT ATOMIC   pas de validation/invalidation automatique, vous définissez
               vos transactions par COMMIT/ROLLBACK.


|    Changer de couleur
 
 II/ déclaratives :
 
 on peut déclarer des variables (sauf les paramètres qui sont déja déclarés)
 
                                          ---DEFAULT NULL-----
    DECLARE ---nom-variable----type-sql---                    -----
                                          ---DEFAULT val/dft--
 
 
 
 on peut assigner des noms aux codes erreurs SQL :
 
    DECLARE ---nom-condition--- CONDITION FOR -----code-erreur
 
 
     code-erreur:       '01234'       = code 1234
                        SQLEXCEPTION  = erreur <0
                        SQLWARNING    = erreur >0 (sauf 100)
                        NOT FOUND     = erreur 100
 
 


|    Changer de couleur
 
 déclaration de condition d'erreur (sorte de MONMSG ..)
 
   on déclare 1/ l'erreur (code ou nom-condition)
 
              2/ le traitement
 
              3/ la reprise après traitement.
                                                             (1)
 
    DECLARE   ------------------------  HANDLER FOR  code-erreur--------->
                !              !
             (3)!--CONTINUE----!
                !              !
                !--EXIT--------!
                !              !
                !--UNDO--------!
 
 
   >-------instruction SQL ;
                 (2)
 


|    Changer de couleur
 
  la reprise :
 
     dans tous les cas on exécute le traitement (2) avant de gérer
      la reprise (3)
 
 
 
     CONTINUE =  on continue le code en séquence.
                 (si l'erreur a lieu dans un IF, CASE, WHILE, on sort
                  et on se débranche à l'instruction qui suit le IF ...)
 
 
     EXIT     =  on va au END
 
 
     UNDO     =  on annule tous les changements (ROLLBACK) puis EXIT,
 
                  ce qui implique  BEGIN ATOMIC !
 
 
 


|    Changer de couleur
 
 III/ Instructions :
 
  toutes les instructions SQL sont valides, aussi bien les ordres de gestion
   de la base (CREATE, GRANT, ...) que les ordres de manipulation.
 
 plus, en assignation :
 
  SET nom-variable = expression (même possibilités que SELECT)
 
 et en structuration  :
 
  IF  --test----THEN--instruction;---------->
 
  >----------------------------------------->
    !                                 !
    !-ELSEIF test2 THEN instruction2;-!
 
  >--------------------------END IF ;------->
    !                    !
    !-ELSE instruction3;-!
 


|    Changer de couleur
 
CASE
 
1ere forme
 
  CASE--nom-variable----------------------->
 
  >---WHEN valeur1 THEN instruction1;------>
 
   >----------------------------------------->
     !                                !
     !-WHEN valeur2 THEN instruction2;!
 
    >------------------------END CASE;
     !                    !
     !-ELSE instruction3;-!
 
 
 
  chaque test se fait par égalité et sur la même variable.
 
 


|    Changer de couleur
 
CASE
 
2eme forme
 
  CASE------------------------------------->
 
  >---WHEN test1---THEN instruction1;------>
 
   >----------------------------------------->
     !                              !
     !-WHEN test2 THEN instruction2;!
 
    >------------------------END CASE;
     !                    !
     !-ELSE instruction3;-!
 
 chaque test peut utiliser :
 
             - un autre opérateur que l'égalité.
             - une variable différente.
 


|    Changer de couleur
 
LOOP (boucle)
 
    -----------------LOOP---------->
     !-label:--!
 
     série d'instructions SQL;----->
 
    ----END LOOP;
 
le seul moyen de sortir de la boucle est LEAVE
 
WHILE (boucle conditionnée, tant que)
 
    -----------------WHILE--condition pour boucler--- DO ---->
     !-label:--!
 
     série d'instructions SQL;----->
 
    ----END WHILE;
 
 la condition est testée AVANT de faire.


|    Changer de couleur
 
 
REPEAT (boucle conditionnée, jusqu'à)
 
    -----------------REPEAT ---->
     !-label:--!
 
     série d'instructions SQL;----->
 
    --UNTIL (condition d'arret)
 
    ----END REPEAT;
 
 la condition est testée en fin de boucle (après).
 
 
  LEAVE
 
  ----LEAVE label-------->
 
      sortie de la boucle qui s'appelle "label"
 


|    Changer de couleur
 
FOR  (exécution d'une série d'instructions pour chaque ligne d'un curseur)
 
     -------------FOR nomf -------------------------------------->
      !-label:--!           !                                 !
                            !--AS nomcurseur CURSOR FOR-------!
 
    ----------clause SELECT------------- DO -------------->
 
     série d'instructions SQL;----->
 
    END FOR;
 
si vous utilisez un nom de curseur il doit être unique
   (non déclaré par DECLARE CURSOR)
sinon, il sera déclaré implicitement.
 
toutes les variables retournées par le SELECT doivent être unique (jointure)
si vous utilisez AS [nocli AS codeclient] c'est "codeclient" que vous devez
    manipuler dans la boucle.
 
et enfin "nomf" n'est pas utilisable dans la série d'instructions.


|    Changer de couleur
 
  l'ordre FOR remplace la série suivante :
 
 ..............................................................
 :   DECLARE ... pour chaque variable du SELECT               :
 :                                                            :
 :   DECLARE C1 CURSOR FOR (votre SELECT)                     :
 :                                                            :
 :   OPEN C1;                                                 :
 :                                                            :
 :   FETCH C1 INTO (les variables déclarées plus haut);       :
 :                                                            :
 :   WHILE (fin de fichier non rencontrée)                    :
 :                                                            :
 :     ---vos instructions SQL;---                            :
 :                                                            :
 :     FETCH C1 INTO (les variables déclarées plus haut);     :
 :                                                            :
 :   END WHILE;                                               :
 :                                                            :
 :   CLOSE C1 ;                                               :
 :............................................................:


|    Changer de couleur
 
 Quelques exemples :
 
 
 FOR :
 
 BEGIN
 DECLARE nomlong  CHAR(40);
          FOR vl AS
              c1 CURSOR FOR
            SELECT prenom , init , nom  FROM personp1
             DO
               SET nomlong  =
                  nom  concat ', ' concat prenom concat
                       ' (' concat init concat ')';
               INSERT INTO TNOMS VALUE ( nomlong );
          END FOR;
 END
 
 
 
 


|    Changer de couleur
 
  avec des curseurs :
 
  ins_loop:
            LOOP
               CALL RETURN_serviveINFO (SERVICE, SERVNOM, SERVADM);
               IF SERVICE IS NULL THEN
                 LEAVE ins_loop;
               ELSE
                 INSERT INTO DEPARTMENT (SERVICE , SERVNOM, SERVADM) ;
            END LOOP;
 
  fetch_loop:
            LOOP
               FETCH c1 INTO
                   v_prenom, v_init,
                   v_nom
               IF SQLCODE <> 0 THEN
                  LEAVE fetch_loop;
            END LOOP fetch_loop;
            CLOSE c1;
 


|    Changer de couleur
 
 BEGIN
 DECLARE n_salaire  DECIMAL(9,2);
 DECLARE v_nopers CHAR(6) DEFAULT '123456';
 
             SELECT salaire
                  INTO n_salaire
                  FROM personp1
                  WHERE nopers = v_nopers;
               IF code  = 1
                THEN SET n_salaire =
                             n_salaire + (n_salaire * 0,10);
                ELSEIF code  = 2
                THEN SET n_salaire =
                             n_salaire + (n_salaire * 0,05);
               END IF;
 
               UPDATE personp1
                  SET salaire = n_salaire
                  WHERE nopers = v_nopers;
 END
 


|    Changer de couleur
 
Et enfin, :
 
CREATE PROCEDURE PROC1 () LANGUAGE SQL
 BEGIN
 DECLARE v_prenom VARCHAR(12);
 DECLARE v_init CHAR(1);
 DECLARE v_nom VARCHAR(15);
 DECLARE v_coef SMALLINT;
 DECLARE v_salaire DECIMAL(9,2);
 DECLARE wEOF INT DEFAULT 0;
 
  DECLARE not_found
                   CONDITION FOR '02000';
  DECLARE c1 CURSOR FOR
              SELECT prenom, init, nom,
                                coef, salaire
              FROM personp1;
  DECLARE CONTINUE HANDLER FOR not_found
                        SET wEOF = 1;
 
 


|    Changer de couleur
 
 
            OPEN c1;
 
            FETCH c1 INTO v_prenom, v_init,
                        v_nom, v_coef, v_salaire;
             WHILE wEOF = 0 DO
 
 
                (traitement de la ligne lue)
 
 
                FETCH c1 INTO
                             v_prenom, v_init,
                             v_nom, v_coef, v_salaire;
             END WHILE;
            CLOSE c1;
 
 END
 
 
 


|    Changer de couleur
 
   Fonctions Définies par l'utilisateur (V4R40) :
  ------------------------------------------------
 
une fonction est un programme ou une procédure dans un programme de service
 enregistré(e) dans les catalogues SQL par CREATE FUNCTION.
 
 par exemple :
 
    CREATE FUNCTION AF4TEST/DT8_CHAR8 (DEC(8 , 0) )
                              RETURNS  CHAR(8)
           EXTERNAL NAME 'AF4TEST/DT_SQL(DT8_CHAR8)'    (1)
           PARAMETER STYLE GENERAL                      (2)
           RETURNS NULL ON NULL INPUT  ;                (3)
 
(1) fait référence à DT8_CHAR8 dans DT_SQL
 
(2) le passage de paramètres se fait sans gestion de la val. nulle
 
(3) la fonction retourne nul si un des argument est nul
     (il n'y aura pas d'appel)
 


|    Changer de couleur
 
 voici le source RPG de la procédure associée à cette fonction :
 
 H nomain
  * prototype  =========================================================
 D dt8_char8       pr             8
 D                                8  0
  * fonction  ==========================================================
 Pdt8_char8        b                   export
 d                 pi             8
 d dt8_recue                      8  0
 
 d date            s               D   datfmt(*dmy/)
 c     *iso          test(d e)               dt8_recue
 
 c                   if        %error
 c                   return    '01/01/40'
 c                   else
 c     *iso          move      dt8_recue     date
 c                   return    %char(date)
 c                   endif
 p                 e


|    Changer de couleur
 
la création se réalise par :
 
   CRTRPGMOD MODULE(DT8_CHAR8)
 et
   CRTSRVPGM SRVPGM(DT_SQL) MODULE(DT8_CHAR8) EXPORT(*ALL)
___________________________________________________________________________
 
 cela permet maintenant de passer des ordres SQL comme :
 
     [soit un fichier client avec DATCRT DEC(8 , 0) ]
 
 SELECT * FROM  CLIENTS WHERE DT8_CHAR8(DATCRT) <> '01/01/40'
 
 SELECT * FROM CLIENTS WHERE DATE( DT8_CHAR8(DATCRT) ) = current date
 
  ..................................................................
  :                                                                :
  : la fonction SQL date attend une chaîne de caractères au format :
  :   du JOB, soit en interactif 'JJ/MM/AA'.                       :
  :                                                                :
  :................................................................:


|    Changer de couleur
 
 Deuxième exemple retournant une date au format caractère en ISO
 
d dt8_char10      pr            10
d                                8  0
 
Pdt8_char10       b                   export
d                 pi            10
d dt8_recue                      8  0
 
d date            s               D   datfmt(*iso)
c     *iso          test(d e)               dt8_recue
 
c                   if        %error
c                   return    '0001-01-01'
c                   else
c     *iso          move      dt8_recue     date
c                   return    %char(date)
c                   endif
c
P                 e
 


|    Changer de couleur
 
déclaration :
 
 
 CREATE FUNCTION AF4TEST/DT8_CHAR10 (DEC(8 , 0) )
                           RETURNS  CHAR(10)
        EXTERNAL NAME 'AF4TEST/DT_SQL(DT8_CHAR10)'
        PARAMETER STYLE GENERAL
        RETURNS NULL ON NULL INPUT  ;
 
 lors de la déclaration de la fonction vous pouvez convertir (caster)
   un/les argument(s) ou la valeur retour :
 
 CREATE FUNCTION AF4TEST/DT8_DATE (DEC(8 , 0) )
                           RETURNS  DATE CAST FROM CHAR(10)
        EXTERNAL NAME 'AF4TEST/DT_SQL(DT8_CHAR10)'
        PARAMETER STYLE GENERAL
        RETURNS NULL ON NULL INPUT  ;
 
 ce qui permet directement :
 
  SELECT * FROM CLIENTS WHERE DT8_DATE(DATCRT) = current date


|    Changer de couleur
 
 Vous pouvez aussi créer des fonctions à l'aide du PL/SQL
 
 create function af4test/euro_francs (VALEUR decimal(9, 2))
                 returns decimal(9, 2)
 LANGUAGE SQL RETURNS NULL ON NULL INPUT
 
 BEGIN (1)
(3) DECLARE VALFRANCS DECIMAL ( 9 , 2 ) ;(2)
(4) SET VALFRANCS= VALEUR * 6.55957;
(5) RETURN VALFRANCS ;
 END;
 
(1)  le corps de la fonction est encadré de BEGIN  - END;
 
(2) chaque ligne se termine par ;
 
(3) les déclarations se font par DECLARE (même types que CREATE TABLE)
 
(4) les manipulations se font par SET ou VALUES
 
(5) RETURN termine la fontion en indiquant la valeur retrounée.





©AF400