V5R40 : intégration d'ordres SQL en format libre, dans le RPG free Avant : /free ... exsr calnbv; ... /end-free C calnbv begsr C/EXEC SQL C+ SELECT cast(COUNT(*) as dec(3 , 0)) INTO :NBV FROM VINS C+ WHERE PR_CODE = :PR_CODE C/END-EXEC Maintenant : /free exec sql SELECT cast(COUNT(*) as dec(3 , 0)) INTO :NBV FROM VINS WHERE PR_CODE = :PR_CODE ; |
Ce qui va être traduit par le pré-compilateur, par : //****xec sql //****SELECT cast(COUNT(*) as dec(3 , 0)) INTO :NBV FROM VINS //**** WHERE PR_CODE = :PR_CODE ; /END-FREE C EVAL SQL_00005 = PR_CODE C Z-ADD -4 SQLER6 C CALL SQLROUTE C PARM SQLCA C PARM SQL_00000 C SQL_00003 IFEQ '1' C EVAL NBV = SQL_00006 C END /FREE Il n'y a toujours qu'une seule instruction SQL par ordre exec sql . SEU vérifie la syntaxe du RPG4 en format libre (avec ou sans SQL) |
Autre nouveautés EVAL-CORR, qui copie d'une structure à l'autre (DS, format, ...) toutes les zones (compatibles) ayant le même nom . Exemple, soit : Dds1 ds qualified D Z1 3P 0 D Z2 7 D Z3 n D Z4 10 et Dds2 ds qualified D Z1 3P 0 D Z2 7 D Z3 n D Z5 10 |
avec EVAL-CORR DS2 = DS1; les zones Z1, Z2 et Z3 sont copiées, le compilateur le signale : Récapitulatif EVAL-CORR 1 29 (N° d'instruction) Z1 Affecté ; correspondance exacte Z2 Affecté ; correspondance exacte Z3 Affecté ; correspondance exacte *RNF7341 Z5 Dans la cible uniquement. *RNF7342 Z4 Dans la source uniquement. avec EVAL-CORR DS3 = DS1; Dds3 ds qualified D Z1 3S 0 D Z2 12 varying D Z3 n D Z5 10 les même zones sont copiées, elles sont compatibles : |
Z1 Affecté ; cible et source sont compatibles Z2 Affecté ; cible et source sont compatibles Z3 Affecté ; correspondance exacte *RNF7341 Z5 Dans la cible uniquement. *RNF7342 Z4 Dans la source uniquement. mais avec EVAL-CORR DS4 = DS1; Dds4 ds qualified D Z1 3 0 D Z2 D D Z3 1 0 D Z5 10 seules la zone Z1 est copiée (il n'y a pas d'erreur à l'exécution) Z1 Affecté ; cible et source sont compatibles *RNF7349 Z2 Le type de données est différent dans la source *RNF7349 Z3 Le type de données est différent dans la source *RNF7341 Z5 Dans la cible uniquement. *RNF7342 Z4 Dans la source uniquement. |
Grosse nouveauté V5R4, très intéressante, le support (en lecture) de XML Les règles du jeu XML Elles sont extrêmement simples. Les informations doivent être : * soit encadrées par des balises ouvrantes(ex. <LIVRE>) et fermantes (ex. </LIVRE>) (contrairement à HTML où ces dernières n'étaient pas toujours obligatoires). On parle alors d'éléments. Les éléments doivent s'imbriquer proprement les uns dans les autres : aucun chevauchement n'est autorisé. Les éléments vides sont permis, selon le format <ELEMENTVIDE/>. * soit incluses à l'intérieur même des balises : on parle alors d'attributs <Exemple : <LIVRE SUJET="XML">. Ici l'attribut SUJET de l'élément LIVRE a la valeur "XML". En XML, contrairement à HTML, les valeurs des entités doivent toujours être encadrées par des guillemets (simples ou doubles) |
* Soit encore définies sous forme d'entités. Les entités sont des abréviations. Par ex; si "Extensible Markup Language" est déclaré comme une entité associée à la notation "xml"; cette chaîne de caractères pourra être abrégée en "&xml;" dans tout le fichier. Une entité peut aussi représenter un fichier XML externe tout entier. (inclusion de fichier XML) La structure arborescente du document XML peut être déclarée formellement dans le corps du document XML ou dans un fichier à part. Historiquement on utilisait des DTD, aujourd'hui on utilise plutôt des shémas XML, qui sont des fichiers (XML) décrivant la structure. ==> Liste des zones, type, obligatoire/facultative, etc... Lorsque qu'un document respecte cette description (DTD ou schéma) on dit qu'il est valide, lorsqu'il respecte uniquement les règles XML on dit qu'il est bien formé. |
Exemple : <AF400 COPYRIGHT="VOLUBIS"> <COURS NOM="£EAU" MODULE="INIT"> <TEXTE>Espace adressable Unique</TEXTE> <MOT_DIRECTEUR> <MOTCLE1>EAU</MOTCLE1> <MOTCLE2>MEMOIRE</MOTCLE2> <MOTCLE3>VIRTUELLE</MOTCLE3> </MOT_DIRECTEUR> <DATREF>1999-08-10</DATREF> </COURS> <COURS NOM="£OBJET" MODULE="INIT"> <TEXTE>Notion d'objet de l'OS/400</TEXTE> <MOT_DIRECTEUR> <MOTCLE1>OS/400</MOTCLE1> <MOTCLE2>OBJET</MOTCLE2> <MOTCLE3>ECAPSULATION</MOTCLE3> </MOT_DIRECTEUR> <DATREF>2002-05-11</DATREF> </COURS> ... </AF400> |
Le nouveau Code Opération XML-INTO, permet de lire un flux XML. - A partir d'une variable ou d'un fichier IFS - dans une variable ou en indiquant une procédure à appeller XML-INTO récepteur %XML(origine : 'options') le récepteur peut-être une variable (simple ou DS à n dimensions) ou bien une procédure désignée par la fonction %HANDLER() qui sera invoquée x fois (cas ou le nombre d'éléments est inconnu à l'avance) la fontion %XML admet en argument : un nom de variable contenant du Xml (option 'doc=string') un nom de variable ou une chaîne représentant les coordonées d'un fichier IFS à lire (option 'doc=file') ansi que des options d'utilisation du parser (routine de lecture XML) |
options valides (le défaut est souligné) +-------------+-----------------+-----------------------------------------+ ! doc ! doc=string ! string : la source est une variable ! ! ! doc=file ! contenant du XML ! ! ! ! file : la source est un fichier IFS ! +-------------+-----------------+-----------------------------------------+ ! case ! case=lower ! lower : le flux XML est en minuscules ! ! ! case=upper ! upper : le flux est en majuscules ! ! ! case=any ! any : le flux XML doit être converti ! ! ! ! avant d'être traité. ! +-------------+-----------------+-----------------------------------------+ ! trim ! trim=all ! all : les espaces d'extrémité des ! ! ! trim=none ! informations caractères sont ! ! ! ! enlevés ! ! ! ! none : ils sont conservés ! +-------------+-----------------+-----------------------------------------+ ! allowmissing! allowmissing=no ! peut-il y avoir des sous zones de DS ! ! ! allowmissing=yes! en plus, c.a.d n'ayant pas d'équivalent! ! ! ! en tant qu'élément XML ! +-------------+-----------------+-----------------------------------------+ |
+-------------+-----------------+-----------------------------------------+ ! allowextra ! allowextra=no ! peut-il y avoir des éléments XML ! ! ! allowextra=yes ! n'ayant pas d'équivalent dans la DS ? ! ! ! ! (le complément de allowmissing) ! +-------------+-----------------+-----------------------------------------+ ! path ! path=noeud/elem ! chemin de l'élément à extraire ! ! ! ! ! ! ! ! dans ce flux ! ! ! ! ! ! ! ! <cours> ! ! ! ! <nom>EAU</nom> ! ! ! ! <module>INIT</module> ! ! ! ! </cours> ! ! ! ! ! ! ! ! si votre DS se nomme COURS et contient ! ! ! ! 2 zones NOM et MODULE, tout est ! ! ! ! automatique, sinon mettez 'path=cours' ! ! ! ! ! ! ! ! pour lire directement le nom dans une ! ! ! ! variable simple (pas une DS), indiquez ! ! ! ! 'path = cours/nom' ! +-------------+-----------------+-----------------------------------------+ |
quelques exemples de code : * * test avec le nom de DS = nom de l'élément * il faut que le nom de l'élément soit en minuscule * dans le XML, sauf à indiquer l'option case=upper ou case=any * Ddata S 1024 inz('+ D <?xml version="1.0" + D encoding="ISO-8859-1"?> + D <cours nom="XML" module="PGM"> + D </cours> + D ') DCOURS DS D NOM 10 D MODULE 10 /free xml-into cours %xml(data) ; // ou bien %xml(data : 'doc=string') *inlr = *on; /end-free |
* * test avec le nom de DS <> de l'élément, il faut indiquer path= * * Ddata S 1024 inz('+ D <cours nom="XML" module="PGM"> + D </cours> + D ') DCOURSDS DS D NOM 10 D MODULE 10 /free xml-into coursds %xml(data : 'path=cours'); *inlr = *on; /end-free |
* * test avec des attributs et plusieurs éléments * * Ddata S 1024 inz('+ D <cours nom="XML" module="PGM"> + D <sujet>RPG</sujet> + D <texte>manipuler du XML en RPG + D </texte> + D </cours> + D ') DCOURS DS D NOM 10 D MODULE 10 D SUJET 10 D TEXTE 50 /free xml-into cours %xml(data); *inlr = *on; /end-free |
* * test avec une structure imbriquée * * Ddata S 1024 inz('+ D <cours nom="XML" module="PGM"> + D <sujet>RPG</sujet> + D <texte>manipuler du XML en RPG + D </texte> + D <motcle> + D <motcl1>RPG</motcl1> + D <motcl2>XML</motcl2> + D <motcl3>SAX</motcl3> + D </motcle> + D </cours> + D ') DmotclDS DS D MOTCL1 10 D MOTCL2 10 D MOTCL3 10 |
DCOURS DS Qualified D NOM 10 D MODULE 10 D SUJET 10 D TEXTE 50 D MOTCLE likeds(motclDS) /free xml-into cours %xml(data); *inlr = *on; //*** en debug *** // COURS.NOM = 'XML ' // COURS.MODULE = 'PGM ' // COURS.SUJET = 'RPG ' // COURS.TEXTE = 'manipuler du XML en RPG ' // COURS.MOTCLE.MOTCL1 = 'RPG ' // COURS.MOTCLE.MOTCL2 = 'XML ' // COURS.MOTCLE.MOTCL3 = 'SAX ' /end-free |
* * test avec plusieurs occurrences * * Ddata S 2048 inz('+ D <af400 copyright="Volubis"> + D <cours nom="XML" module="PGM"> + D <sujet>RPG</sujet> + D <texte>manipuler du XML en RPG + D </texte> + D <motcle> + D <motcl1>RPG</motcl1> + D <motcl2>XML</motcl2> + D <motcl3>SAX</motcl3> + D </motcle> + D </cours> + D <cours nom="SQL" module="PGM"> + D <sujet>SQL</sujet> + D <texte>nouveautés SQL en V5R40 + D </texte> + |
D <motcle> + D <motcl1>RECURSIF</motcl1> + D <motcl2>RANK</motcl2> + D <motcl3>OVER</motcl3> + D </motcle> + D </cours> + D <cours nom="I5OS" module="SYS"> + D <sujet>V5R4</sujet> + D <texte>nouveautés 5.40 de I5/SO + D </texte> + D <motcle> + D <motcl1>OS</motcl1> + D <motcl2>I5</motcl2> + D <motcl3>OS4OO</motcl3> + D </motcle> + D </cours> + D </af400> + D ') |
DmotclDS DS D MOTCL1 10 D MOTCL2 10 D MOTCL3 10 DCOURS DS Qualified dim(50) D NOM 10 D MODULE 10 D SUJET 10 D TEXTE 50 D MOTCLE likeds(motclDS) /free // la DS est déclarée DIM(50), et il n'y a que 3 éléments, cela ne // pose pas de problèmes, les 3 premières occurrences sont remplies xml-into cours %xml(data : 'path=af400/cours'); *inlr = *on; /end-free |
* * test avec un fichier XML externe * * DmotclDS DS D MOTCLE1 10 D MOTCLE2 10 D MOTCLE3 10 D MOTCLE4 10 D MOTCLE5 10 DCOURS DS Qualified dim(50) D NOM 10 D MODULE 10 D TEXTE 50 D TYPE 10 D SRCFIL 10 D SRCLIB 10 D SRCMBR 10 D CHEMIN 80 |
D SUJET 10 D MOT_DIRECTEUR likeds(motclDS) D DATREF D /free // la DS est déclarée DIM(50), et il n'y a bien plus d'éléments dans // le fichier IFS, cela ne pose pas de problèmes, seules les 50 // premières occurrences sont remplies xml-into cours %xml('/af4dir/courshtm/xml/cours.xml' : 'doc=file case=upper path=AF400/COURS') ; *inlr = *on; /end-free LA V6R10 ammenera les DS à 16 Mo, en attendant on peut utiliser les *USRSPC (User Spaces) qui eux font déja 16Mo, sinon vous êtes limités aux 65535 octets du RPG4 V5. Pour éviter cela, utilisons les %Handler du code opération xml-into: |
H dftactgrp(*no) actgrp(*caller) * * test avec un fichier XML externe et un %Handler * (voir http://www.volubis.fr/af4dir/courshtm/xml/cours.xml ) * DmotclDS DS D MOTCLE1 10 D MOTCLE2 10 D MOTCLE3 10 D MOTCLE4 10 D MOTCLE5 10 DCOURS DS Qualified D NOM 10 D MODULE 10 D TEXTE 50 D TYPE 10 D SRCFIL 10 D SRCLIB 10 D SRCMBR 10 D CHEMIN 80 D SUJET 10 |
D MOT_DIRECTEUR LikeDS(motclDS) D DATREF D D flagERR S n * prototype de la procédure déclarée %handler() Dtrt10cours PR 10I 0 D flagERR n D cours10 likeds(cours) D dim(10) D CONST D nbcours 10I 0 value /free xml-into %HANDLER(trt10cours : flagERR) %XML('/af4dir/courshtm/xml/cours.xml' : 'doc=file case=upper path=AF400/COURS') ; if flagERR ; //// endif; *inlr = *on; /end-free |
Ptrt10cours B * paramètres en entrée D PI 10I 0 D flagERR n D cours10 likeds(cours) D dim(10) D CONST D nbcours 10I 0 value * variables locales D i S 10I 0 D plusgrandeDate S D static /free for i = 1 to nbcours; Monitor; if cours10(i).datref > plusgrandedate; plusgrandedate = cours10(i).datref; endif; |
on-error *all; flagERR = *ON; endmon; endfor; if flagERR = *ON; return -1; else; return 0; endif; /end-free Ptrt10cours E La documentation précise que la procédure doit avoir le prototype suivant : D handler PR 10I 0 D commarea ? D uneDS dim(??) D CONST D nbelem 10U 0 value il y a un bug en début de version obligeant à saisir ce dernier 10I 0 |
la zone commarea est une zone de communication entre votre programme et le handler, ce doit être un passage de paramètre par référence ("normal") le type peut-être n'importe quoi (un booléen, une chaîne, ...) le deuxième paramètre, doit être la DS de réception des données à N dimensions. Le handler est appellé, autant de fois que nécessaire, en lui transmettant N éléments, sauf la dernière fois ou cela risque d'être moins. Le troisième paramètre contient le nombre d'éléments transmis (obligatoire) lors du dernier appel.Le passage de paramètre se fait par valeur. Enfin, la procédure, doit retourner une valeur indiquant si tout c'est bien passé. Si la valeur retournée n'est pas 0, le traitement s'arrete. |
Dernier point, vous avez un accès direct au parser (plus compliqué) par le biais du code opération XML-SAX Par exemple, pour le flux XML suivant <cours nom="EAU"> <datref>1999-08-10</datref> </cours> votre Hanlder est appellé 1/ en début de traitement 2/ pour le début d'élément <cours 3/ pour l'attribut nom= 4/ pour la valeur de l'attribut ("EAU") 5/ pour la fin de l'élément 6/ pour le début de l'élément <datref> 7/ pour la valeur de l'élément (1999-08-10) etc, etc... le handler doit recevoir alors 4 paramètres (et non 3) |
D handler PR 10I 0 D evenement 10I 0 value D chaine * value D chaine_lg 20I 0 value D exception 10I 0 value "evenement" pouvant prendre l'une des valeurs suivantes : (les équivalents numériques sont entre parenthèses) *XML_ATTR_UCS2_REF (1) *XML_ATTR_NAME (2) *XML_ATTR_PREDEF_REF (3) *XML_ATTR_CHARS (4) *XML_CHARS (5) *XML_COMMENT (6) *XML_UCS2_REF (7) *XML_PREDEF_REF (8) *XML_DOCTYPE_DECL (9) *XML_ENCODING_DECL (10) *XML_END_CDATA (11) *XML_END_DOCUMENT (12) *XML_END_ELEMENT (13) *XML_END_PREFIX_MAPPING (14) *XML_EXCEPTION (15) *XML_PI_TARGET (16) *XML_PI_DATA (17) *XML_STANDALONE_DECL(18) *XML_START_CDATA(19) *XML_START_DOCUMENT (20) *XML_START_ELEMENT (21) *XML_START_PREFIX_MAPPING (21) *XML_UNKNOWN_ATTR_REF(23)*XML_UNKNOWN_REF(24) *XML_VERSION_INFO(25) *XML_END_ATTR(26) , qui sont de nouveaux mots réservés du RPG pour plus de détails, voyez http://www.saxproject.org et particulièrement http://www.saxproject.org/event.html sur la notion d'évenement. |
Le mot-clé DEBUG() prend de nouvelles valeurs avant la V5R40 seules les options *YES|*NO étaient possibles activant ou non, le code opération DUMP. V5R40 *XMLSAX , rend actif un tableau _QRNU_XMLSAX( ) contenant les événements XML possibles sans le préfix "*XML_" (si le programme est compilé avec DBGVIEW(*SOURCE) ou plus) par exemple _QRNU_XMLSAX(1) = 'ATTR_UCS2_REF ' le but est que vous puissiez demander les différents codes possibles en tapant EVAL _QRNU_XMLSAX en debug. *INPUT , les zones en entrées des fichiers en lecture, sont placées en mémoire, même si elles ne sont pas manipulées par le pgm. *DUMP , le code opération DUMP est actif. |
Et enfin, dernière nouveauté, le support des variables à valeur nulle, possible sur les DS externes (E DS ou définies avec LIKERCD) Si le fichier qui sert de modèle contient des zones à valeur nulle possible , les même zones ont aussi la même possibilité dans la DS. Cet attribut est transmis lors de la définition d'une DS par LIKEDS (mais est perdu lors de la définition d'une zone simple par LIKE) Cet attribut est copié lors d'un EVAL-CORR. Vous pouvez voir la valeur nulle en DEBUG, en demandant EVAL _QRNU_NULL_NomdeDs.NomdeZone ou toutes les valeurs nulle par EVAL _QRNU_NULL_NomdeDs Si une telle DS (contenant des zones à valeur nulle) ou une variable simple à valeur nulle possible, est transmise en tant que paramètre, OPTIONS(*NULLIND) sur le prototype permet de transmetre la valeur nulle aussi bien que la valeur elle même, à la procédure appellée. |
V6R10 , la ptf SI34938 apporte deux nouvelles options à xml-into 1/ datasubf permet de régler le problème suivant: Ddata S 1024 inz('+ D <cours nom="XML" module="PGM"> D manipuler du XML en RPG D </cours> D ') La balise cours contient deux attributs nom et module, mais aussi une donnée directement encadrée par la balise (ici le texte descriptif) l'option datasubf=text ("text" est un exemple), permet de recevoir les données dans une DS de ce type : DCOURS DS D NOM 10 D MODULE 10 D TEXT 50 |
2/ countprefix permet de recevoir le nombre d'éléments extrait dans un compteur. Ddata S 1024 inz('+ D <les cours> D <cours nom="XML" module="PGM"> D </cours> D <cours nom="XML" module="RPG"> D </cours> D <cours nom="XMl" module ="GAP"> D </cours></lescours>') countprefix=nbr, permet d'avoir une DS de ce type associée à xml-into Dlescours Dnbrcours 10I 0 Dcours likeds(cours_modele) DIM(50) la variable nbrcours contenant ensuite la valeur 3. (voir http://www-949.ibm.com/software/rational/cafe/docs/DOC-2975 ) |
V6R10 , la ptf SI42426 apporte trois autres options à xml-into 3/ ns , gestion des namespaces (espaces de nommage) parfois la structure XMl utilise les namespaces, pour "qualifer" les noms: Ddata S 1024 inz('+ D <les cours xmlns:vNS= D "http://www.volubis.fr/NS"> D <vNS:cours nom=="XML" module="PGM"> D </cours> D <vNS:cours nom="XML" module="RPG"> D </cours> D <vNS:cours nom="XMl" module ="GAP"> D </cours></lescours>') ns=remove enlève les espaces de nommage pour rechercher les sous-zones (la data structure s'appelle cours) ns=merge ajoute l'espace de nommage en remplacant ":" par "_" (la data structure s'appelle vNS_cours) |
4/ ns_prefix, récupération du namespace nsprefix=namespc_ si on trouve une zone namespc_xxx, où xxx est le nom de la zone cette dernière recevra le namespace, soit "vNS" dans nos exemples. exemple DCOURS DS D NOM 10 D namespc_NOM 32 D MODULE 10 D namespc_MODULE 32 5/ case=convert gère les balises XML portant des noms, non valides en RPG (code-postal) les noms sont convertit en majuscules (comme case=any),les caractères invalides remplacés par '_', sauf le première caractère qui est ignoré s'il est invalide. (code-postal devient CODE_POSTAL) |