Pause-Café Volubis

pause-café

rendez-vous technique
Pause-Café est une réunion technique
destinée aux informaticiens sur plateforme IBM i.
Elle a lieu 3 à 4 fois par an : en Bretagne et sur internet.

Pause-café #45

Novembre 2007

Associé à MySQL, le langage PHP permet de développer des applications web puissantes en réutilisant des produits standards, souvent en mode "open source"

 

Novembre 2007, Zend annonce (en béta) Neon, successeur de Zend Studio, , basé sur Eclipse 3

  • cela amène un éditeur PHP comparable à Zend Studio, dans Eclipse (donc bientôt WDS client)


  • mais aussi un éditeur HTML, WYSIWYG


    Les vues traditionnelles d'Eclipse, étant utilisées au mieux
    • Vue Outline (structure)
    • Vue propriétés


Pour installer MYSQL, voyez http://www.zend.com/forums/index.php?t=msg&goto=9216&S=2a88332173cb39a2ac482b1e88c526b8

en un mot :

 - Téléchargez la version AIX-64 Bits compatible OS/400 à restaurez (untar) les fichiers

- créez un utilisateur (MYSQL) et donnez lui tous les droits sur le répertoire
/QOpenSys/usr/local/var/mysqlxxx/data

- lancez le script mysql_install_db

- lancez MYSQL par /QOpenSys/usr/local/var/mysqlxxx/bin/mysqld_safe

- vous pourrez utiliser MYSQL en mode commande :
 


Mais vous pouvez aussi, (surtout) maintenant, utiliser des produits écrits en php et utilisant mysql

PHPMyAdmin

ou


Mediawiki

 

Ansi que Joomla, sugarCRM, phpNuke, etc...

Petit nouveau dans cette liste : Mantis, produit de gestion et de suivi de bugs et d'incidents, en équipe,
pour lequel on vous propose une version spécifiquement System i , utilisant DB2 sur mantis400.com

 

Téléchargez l'image ISO du CD et lancez LODRUN OPTxx sur une session 5250, confirmez la licence (Open source),
le produit est installé dans www/zendcore/htdocs/mantis400

 

depuis votre navigateur, tapez http://votre-as400:89/mantis400/, la page suivante doit vous être affichée :

 

Saisissez les données suivantes

où :

- AS400 est le nom de votre base de données (enregistré oar WRKRDBDIRE),
- AS400/MANTIS le nom de la base suivi de la biblliothèque à créé
- MANTIS est le nom d'un utilisateur valide.

SI tout ce passe bien, vous devez voir :


LA bibliothèque MANTIS a été créé, renseignez le fichier config_inc.php comme indiqué ici, puis loggez vous administrator/root

 


 

la première chose à faire est de cliquer sur "manage", afin de créer un autre administateur, puis un premier projet.

RPG-IV, presque 5

 

Quelques règles simples que nous vous proposons d'instaurer autour du RPG4 :

  • déclarer vos compteurs etc.. comme des zones 5I   0 ou 10I   0, quand vous n'avez pas besoin de décimales

  • déclarer vos variables relativement à la base de données avec like (on peut faire montant LIKE(prix) et + 10 en longeur)

  • Utilisez les DS qualifiées, c'est mieux que les DS à occurrences et ca permet plein de choses :
    • les DS dans les DS (la V6 apportera les DS "modèles" avec TEMPLATE)
    • les DS de type tableau ou à dimensions (DIM directement sur la déclaration)
    • les tableaux à double entrée (DS à dimension dans une DS à dimension)

  • Utilisez vos propres indicateurs ou variables booléennes
    • type N , lors de la déclaration
    • associez votre structure de 99 booléens, avec les DSPF à la place des indicateurs avec INDDS(votre-ds) en spécif F
            
      FECRAN02   CF   E             WORKSTN INDDS(indic)

      .../...

       Dindic          DS                   99
       D exit                                 N    overlay(indic:3)
       D sflclr                               N    overlay(indic:30)
       D err_client                           N    overlay(indic:51)
       etc...
              
              
  • Utiliser les fonctions intégrées du langage à chaque fois que c'est possible :

    • For i = 1 to %elem(tbl) // pour passer en revue tous les postes d'un tableau
    • if %subst(nom : %size(nom) : 1) // pour tester le dernier caractère

pour mémoire voici la liste des fonctions

Fonction(x)
Retourne
Exemple
%ADDR l'adresse d'une variable EVAL prt= %addr(data)
%PADDR l'adresse d'une procédure (pour CALLB)  
%ELEM donne le nombre d'occurrences d'un tableau ou d'une DS
if   I < %elem(tbl_tva)
%SUBST extraction d'une chaine de caractère
if   %subst(raisoc : 1 : 1) <> ' '

eval %subst(pgm : 1 : 1) = 'Q'
%SIZE donne la longueur déclarée d'une variable
if    %SUBST(nom : %size(nom): 1) = *blank
%DECPOS donne le nombre de décimales  
%TRIM élimination des espaces d'extrémité
eval NP = %trim(nom) + ' ' + %trim(prenom)
%TRIML|%TRIMR élimination des blancs de gauche/de droite  
%PARMS indique le nombre de paramètres recus
if   %parms > 2 
%ABS valeur absolue  
%DEC
%DECH

transforme une valeur numérique (char en V5.2,date en V5.3) en numérique condensé ou packé.
(%DECH applique un arrondi)

eval DATAMJ = %dec(DATCDE : *YMD)
%INT
%INTH

transforme une valeur numérique (ou caractère en 5.20)au format binaire signé

 
%UNS
%UNSH

transforme une valeur numérique (ou caractère ) au format binaire non signé

 

%FLOAT

transforme une valeur numérique (ou caractère) au format virgule flottante  

%EDITFLT

transforme de virgule flottante vers chaîne  

%EDITC

transforme du numérique en chaine avec application
d'un edit code
eval msg = 'date de traitement' + 
%editc(date : 'Y')

eval montanchar = %editc(
qte * prix : '3')

%EDITW

idem %EDITC mais avec un mot d'édition  
%CHAR transforme date ou numérique en chaîne
ZoneChar=%CHAR(ZoneDec) // 000123,45- ==> '-123,45'
%STR transforme une chaine RPG en chaîne compatible C
(pointeur vers une chaine terminée par x'00' )
 

%LEN

retourne la longueur d'une expression
if   %len(%trim(nom)) > 2
eval L = %len(zone-variable)

%SCAN

recherche si présence d'une chaîne de caractères
eval chaine = 'ILE/RPG-IV' 
eval pos = %scan('I' : chaine) [pos=1]
eval deb = pos + 1
eval pos = %scan('I' : chaine: deb) [pos=9]
%REPLACE remplace par une chaîne l'information située à la position indiquée
                          [var1 = 'AS/400'] 
eval x = %scan('4' : var1)
eval var2 = %replace('5': var1:x)
[var2 = 'AS/500']
eval var2 = %replace('X': var1 : 1 : 3)
[je remplace 3 caractères : var2 = 'X500']

%NULLIND

test la val.nulle sur un champ base de données
IF   %NULLIND(datcmd) = *ON 

EVAL %NULLIND(prime) = *OFF
EVAL prime = 1500

%OPEN

indique si un fichier est ouvert
if   %open(clients)

%EOF

indique si l'on est fin de fichier
dow  not %eof(clients)

%ERROR

indique si on est en erreur
(à utiliser suite à un code avec (E).)
if   %error

%STATUS

indique le n° d'erreur (code status)
if   %error and %status(clients) = 1252

%EQUAL

indique
un positionnement par égalité (SETLL)
une recherche aboutie par égalité (LOOKUP)
 

%FOUND

indique
un positionnement réalisé(SETLL,SETGT)
un recherche aboutie (CHAIN)
 
---- V4R40 ----
 
 

%GRAPH

conversion en DBCS
 

%UCS2

conversion en UNICODE
 

%XFOOT

somme d'un tableau
eval total = %xfoot(tb_salaire) + prime

%DIV

résultat de la division (entiers uniquement)
 

%REM

reste de la division (entiers uniquement)
if   %rem(an : 4 )= 0 
---- V5R10 ----
 
 
%ALLOC l'adresse d'un nouvel espace mémoire EVAL prt= %alloc(nboctets)
%CHECK la position du premier caractère invalide
Eval pos = %check('0123456789.' : variable)
%CHECKR Idem %CHECK, mais en commencant à droite.
 
%DATE convertit une variable (alpha ou dec) en date ou retourne la date du jour.
eval datcmd = %DATE(datchar : *YMD0)//(c'est un zéro)
%DAYS indique un nombre de jours
eval datliv = datcmd + %DAYS(15)
%DIFF exprime un écart entre deux dates ou heures
eval ecart = %DIFF(datliv : datcmd : *DAYS)
%SUBDT extrait une partie d'une date
eval annee = %SUBDT(datliv : *YEARS)
%HOURS exprime un nombre d'heures
eval demain = maintenant + %hours(18)

%LOOKUP
(%LOOKUPxx)

indique si un élément est présent dans un tableau eval pos = %lookup('$' : monnaies)
%MINUTES exprime un nombre de minutes
voir %HOURS()
%MONTHS exprime un nombre de mois
voir %DAYS()
%MSECONDS

exprime un nombre de microsecondes

voir %HOURS
%OCCUR

positionne sur une occurrence de DS

%OCCUR(DS1) = 7 ou x = %occur(DS2)
%REALLOC

Réalloue un espace mémoire plus grand

voir %ALLOC

%SECONDS

exprime un nombre de secondes
voir %HOURS()

%SHTDN

indique si le système est en phase d'arrêt
if %SHTDN

%TIME

convertit une variable (alpha ou dec) en heure ou retourne l'heure en cours.
if %time(pointage : *HMS) = T'23:59:59'

%TLOOKUP[xx]

indique si un élément est présent dans une table GAP
voir %lookup
%XLATE transforme une variable apha
eval resultat = %XLATE('abcdef' : 'ABCDEF' : origine)
%YEARS exprime un nombre d'années
voir %DAYS
---- V5R20 ----
 
 
%KDS Utilisation d'une DS en tant que liste de clés sur un CHAIN.
CHAIN %KDS(dscli) clientf1;
// mais aussi CHAIN (DEPCLI : NOCLI) clientf1;
%FIELDS Mise à jour d'une liste finie de zones lors d'un UPDATE .
Update clientf1 %FIELDS(nomcli : depcli : ville) ;
%BITNOT inverse les bits
%BITNOT(x'00') = x'FF'
%BITOR applique un OU logique bit à bit
%BITOR(x'F0' : x'0F') = x'FF'
%BITAND applique un ET logique bit à bit
%BITAND(x'F1' : x'1F') = x'11'
%BITXOR applique un OU exclusif bit à bit
%BITXOR(x'F1' : x'1F') = x'EE' [1110 1110)
---- V5R30 ----
 
 
%SUBARR une partie d'un tableau
resultat = %xfoot( %subarr(tbl : i : 5) )// 5 postes
---- V5R40 ----
 
 
%XML coordonnées des données XML à lire (cf XML-INTO)
xml-into DS1 %xml(data : 'doc=string')
  • Ecrivez en format libre (nous sommes en 2007 ! )

Quelques propositions pour remplacer les codes opérations qui ne "passent" pas :

Code Opération
Remplacement
Exemple
ADD   + EVAL A = A +1 ou A = A+1 ou A+=1
ADDDUR   + Datliv = datcmd + %DAYS(45)
ALLOC %alloc  
ANDxx    (test en libre) if A=B AND compteur<>0
CABxx   (à éviter)  
CALL CALLP + EXTPGM CALLP pgmA (parm1 : parm2)
CALLB CALLP + EXTPROC    idem
CASxx IF + EXSR  
CAT    + Message = 'date de traitement' + DATCHAR
CHECK %check pos = %check('0123456789-+,' : qtechar)
CHECKR %checkr    idem
*LIKE DEFINE déclaration en D avec LIKE( )  
*DTAARA DEFINE déclaration en D avec DTAARA( )  
DIV    /  
DO FOR FOR i = 1 to 50
DOUxx DOU Dou %eof
DOWxx DOW Dow not %eof
END ENDxx (déja recommandé avant)  
ENDCS    cf CAS  
EXTRCT %subdt mois = %SUBDT(DATCMD : *M)
GOTO    c'est quoi ?       ;-)  
IFxx IF if GAP = 4
KFLD %KDS ou directement (K1 :K2) CHAIN (societe: nocli) clientf1
KLIST     (cf ci-dessus)  
LOOKUP %lookup tva = %lookup(code : tbtva)
MOVE EVALR , Convertir avec les fonction intégrées datcmd = %DATE(datchar:*ISO) ou chaine = %char(datcmd)
MOVEL EVAL, idem pour les conversions  
MOVEA
Pas d'équivalent
 
MULT    *  
MVR %rem if %rem(AN : 4) = 0
OCCUR %occur  
ORxx  cf ANDxx  
PARM Prototype en spécif D *  
PLIST  cf PARM  
REALLAOC %realloac  
SCAN %scan QUATRE = %scan('4' : 'RPG4')
SETOFF *inxx = *off  
SETON *inxx = *on  
SUB    -  
SUBDUR    - HIER = AUJOURDHUI - %days(1)
ou SUBDUR %diff UN = %DIFF(aujourdhui : hier)
SUBST %subst  
TAG    cf GOTO  
TESTN %check if %check('0123456789' : QTECHAR) > 0
TIME %date() ou %time() aujourdhui = %date()
WHENxx WHEN  
XFOOT %xfoot TVA = %xfoot(tbmt) * TAUX
XLATE %xlate GRAND = %xlate(minuscles: majuscules: PETIT)
Z-ADD EVAL  
Z-SUB EVAL -  

  • Utilisez les prototypes, ca sera toujours ça de gagné pour ILE

* CALLP est apparu en V3R60 et permet un appel en format libre :


il faut pour cela déclarer le programme et ses paramètres, avant, en spécif D, cela s'appelle un PROTOTYPE.
  +-----------------------------------------------------------+
! DEXEC PR EXTPGM('QCMDEXC') !
! D 250 const !
! D 15P 5 const !
+-----------------------------------------------------------+ PR indique qu'il s'agit d'un prototype (même positions que DS)
     EXTPGM le nom du pgm externe (peut être qualifié)
     CONST sur un paramètre indique que ce paramètre peut ne PAS 
      être unevariable (constante, calcul, fonction intégrée, ...)
     VALUE passage de paramètre par valeur (pas de valeur retour)
     OPTIONS(*NOPASS) ce paramètre peut ne pas etre transmis
        [tester le nombre de paramètres avec %parms() ]
OPTIONS(*OMIT) ce paramètre peut etre omis [tester le paramètre avec %addr(param) <> *NULL]


Exemple :
/free
exec('WRKSPLF': 7) ;
/end-free

la réception des paramètres peut elle même etre réalisée sur le même principe , avec prototype ET interface de procédure ( PI )
 
       DProgrammeB           PR                  EXTPGM('PROGRAMMEB')
       D                            6  0
DProgrammeB PI D nocli 6 0
 
  
  • Utilisez une gestion fine d'erreur avec MONITOR/ON-ERROR/ENDMON
         
         
         /free
          MONITOR ;
           chain nocli clientf1 ;
           if %found ;
            chaine = %subst( tbl(x) : %scan('*':tbl(x)) + 1)
           endif;      
          On-error 1211 ; // fichier non ouvert
           ...
          On-error *FILE ; // autres erreurs fichier
           ...
          On error 00100:00121 ; // erreur de chaîne ou d'indice
           ....
          On-error *ALL ; // autres erreurs (MONMSG CPF0000)
           ...
          ENDMON;
         /end-free
                            


  • Apprenez ILE, particulièrement les programmes de service

    • pour écrire vos propres fonctions (elles peuvent être communes avec SQL)
    • pour utiliser les fonctions destinées au C (sockets IP, manipulation de fichiers IFS)
    • pour utiliser JAVA (avec JNI) pour, par exemple, accéder à MYSQL ou Oracle en RPG

 



Procédures cataloguées



   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 modifiés par le pgm distant.



 Syntaxe  CALL nom-procédure----------------------------------------------
                                 |                       |
                                 |-(param1,[param2,...])-|




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 ...


 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.




 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.



  L'application cliente (VB, WINDEV, PHP, Java/JDBC, exécute la requête (contenant CALL)
et recoit un paquet d'enregistrements, comme si elle avait lancé un SELECT (movefirst puis movenext, etc...)







 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   -nom-proc--(même syntaxe que DECLARE --)---->

                     ----------------------------------------------.
                       !                                        !
                       !---DYNAMIC RESULT SETS -----n------------
                    
                       
n indique le nombre "result sets" retournés


   La définition est enregistrée dans les fichiers SYSPROCS et SYSPARMS
    du catalogue.



Vous pouvez faire cette déclaration depuis operation navigator

Détail de création d'une procédure cataloguée externe (cliquez pour agrandir l'image)

Nom de la procédure et attributs

Définition des paramètres





nom du programme "externe"


Vous pourrez ensuite générer le source avec cette option :




 Mais, il est aussi 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 appeler le compilateur.







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




 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.




 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






 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)




  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 !








 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;-!




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.






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.




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.






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"




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.




  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 ;                                               :
 :............................................................:

 


Fonctions SQL



   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)





 Là aussi vous pouvez utiliser le SQL PSM.

 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 retournée.

 

Exemples de fonctions SQL, utilisées sur les sites des clubs régionnaux


-- retourne le libellé d'une date

CREATE FUNCTION MABIB.LIBDATE ( DATE_IN DATE )
RETURNS CHAR(27)
LANGUAGE SQL SPECIFIC MABIB.LIBDATE
NOT DETERMINISTIC READS SQL DATA
RETURNS NULL ON NULL INPUT

BEGIN
DECLARE WDATE CHAR ( 27 ) ;
DECLARE MOIS CHAR ( 9 ) ;
DECLARE JOUR CHAR ( 8 ) ;

SET JOUR = CASE DAYOFWEEK( DATE_IN )
WHEN 1 THEN 'DIMANCHE'
WHEN 2 THEN 'LUNDI'
WHEN 3 THEN 'MARDI'
WHEN 4 THEN 'MERCREDI'
WHEN 5 THEN 'JEUDI'
WHEN 6 THEN 'VENDREDI'
WHEN 7 THEN 'SAMEDI'
END;

SET MOIS = CASE SUBSTR ( CHAR ( DATE_IN ) , 6 , 2 )
WHEN '01' THEN 'JANVIER'
WHEN '02' THEN 'FÉVRIER'
WHEN '03' THEN 'MARS'
WHEN '04' THEN 'AVRIL'
WHEN '05' THEN 'MAI'
WHEN '06' THEN 'JUIN'
WHEN '07' THEN 'JUILLET'
WHEN '08' THEN 'AOUT'
WHEN '09' THEN 'SEPTEMBRE'
WHEN '10' THEN 'OCTOBRE'
WHEN '11' THEN 'NOVEMBRE'
WHEN '12' THEN 'DÉCEMBRE'
END ;

SET WDATE = TRIM(JOUR) CONCAT ', '
CONCAT SUBSTR ( CHAR ( DATE_IN ) , 9 , 2 )
CONCAT ' ' CONCAT TRIM(MOIS)
CONCAT ' ' CONCAT SUBSTR ( CHAR ( DATE_IN ) , 1 , 4 ) ;

RETURN WDATE ;
END ;

 


-- retourne une date de jj/mm/aa vers aaaa-mm-jj


CREATE FUNCTION MABIB.INVDATE ( CHAR_IN CHAR(10) )
RETURNS CHAR(10)
LANGUAGE SQL SPECIFIC MABIB.INVDATE
NOT DETERMINISTIC READS SQL DATA
RETURNS NULL ON NULL INPUT

BEGIN
DECLARE WDATE CHAR(10) ;
SET WDATE = CASE
WHEN LENGTH(TRIM(CHAR_IN)) > 8
THEN SUBSTR(CHAR_IN, 7 ,4)
CONCAT '-' CONCAT SUBSTR(CHAR_IN, 4 ,2)
CONCAT '-' CONCAT SUBSTR(CHAR_IN, 1, 2)
WHEN LENGTH(TRIM(CHAR_IN)) > 6
THEN '20' CONCAT SUBSTR(CHAR_IN, 7 ,2)
CONCAT '-' CONCAT SUBSTR(CHAR_IN, 4 ,2)
CONCAT '-' CONCAT SUBSTR(CHAR_IN, 1, 2)
ELSE
'20' CONCAT SUBSTR(CHAR_IN, 5 ,2)
CONCAT '-' CONCAT SUBSTR(CHAR_IN, 3 ,2)
CONCAT '-' CONCAT SUBSTR(CHAR_IN, 1, 2)
END;

RETURN WDATE ;
END ;


Encore une fois, vous pourrez créer votre fonction depuis operation navigator, puis retrouver le source de la déclaration ensuite
    

Triggers en SQL PSM


 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



 + 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 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, des mise à jour de champs, des traces ...

   FOR EACH STATEMENT le trigger est appellé une fois par instruction.
       cas de recalcule automatique de cumuls, etc...
     ATTENTION : le trigger est appellé 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                                                                  :  :......................................................................:


Gestion des erreurs et 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 recu.)


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 commencant 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 commencant 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.

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

Puis propriétés



Top


Copyright © 1995,2007 VOLUBIS