pause-café
destinée aux informaticiens sur plateforme IBM i.
Pause-café #83
TR7/ TR1
Améliorations de la 7.4 apportées par PTF :
TR1 (certaines fonctions sont communes à la TR7 de la 7.3)
IBM continue sur la lancée des versions précédentes en matière de livraison des nouveautés au fil de l'eau.
Ces dernières ont vu des améliorations livrées sous forme de TR, tous les six mois.
La TR1 de la 7.4 coïncide avec la TR7 de la 7.3. il n'y a plus de nouvelles TR en 7.2
- DB2 i
- 7.4 : Lors de l'ajout ou le retrait d'une contrainte d'intégrité référentielle (commandes vers client par exemple)
-> si la table parente est vide, le niveau de verrouillage passe à *EXCLRD (avant il était à *EXCL)
- ALLOW_DDL_CHANGES_WHILE_OPEN dans QAQQINI
- Autorisait déjà l'ajout ou le retrait de triggers quand un fichier est utilisé
- Désormais vous pouvez aussi ajouter ou retrancher des droits
- GRANT et REVOKE (DDL)
- GRTOBJAUT/RVKOBJAUT (cdes CL)
- SUPPRESS_INQUIRY_MESSAGES (QAQQINI toujours)
- ne pas envoyer le message CPA32B2
La modification du fichier &1 peut entraîner une perte de donnée. (C I) ?
- Nouveaux synonymes
- Fonctions
- RANDOM
- STRRIGHT
- STRLEFT
- POW
- TO_CLOB
- STRPOS
- Prédicats (clause WHERE)
- ISNULL
- NOTNULL
- Nouveaux services (voyez ce récapitulatif)
- SYSTOOLS.LPRINTF
écrit un message dans la JOBLOG
- Data QUEUE
- CLEAR_DATA_QUEUE (procédure)
- DATA_QUEUE_INFO (fonction table)
- SEND_DATA_QUEUE (procédure)
- RECEIVE_DATA_QUEUE (fonction table)
un temps d'attente négatif provoque une attente indéfinie.- ILE
- BOUND_MODULE_INFO (vue, mais il y a aussi une UDTF)
Donne la liste des modules d'un programme
- BOUND_SRVPGM_INFO (vue, mais il y a aussi une UDTF)
Donne la liste des programmes de service utilisés par un programme
- PROGRAM_EXPORT_IMPORT_INFO (vue)
liste les variables et les procédures exportées et importées
- PROGRAM_INFO
retourne des informations proches de DSPPGM et DSPSRVPGM(la signature, par exemple)
- IFS
- IFS_JOB_INFO
Fichiers IFS manipulés par un travail
- IFS_OBJECT_LOCK_INFO
liste des travaux verrouillant un fichier de l'IFS
- IFS_OBJECT_REFERENCES_INFO
retourne UNE ligne, avec les informations concernant le fichier de l'IFS indiqué en paramètre
- IFS_OBJECT_STATISTICS
retourne des infos sur l'arborescence d'un répertoire
- options
- subtree-directories NO / YES
- object-type-list
- *ALLDIR : tous les sous -répertoire
- *ALLSTMF : tous les fichiers
- *MBR : tous les membres (/QSYS.LIB)
- *NOQSYS : exlure QSYS
- omit-list
- chemin(s) à exclure, séparés par un espace
- OBJECT_OWNERSHIP (vue)
retourne la liste des objets possédés par un profil.
- Objets de QSYS
- IFS
- la procédure SET_SERVER_SBS_ROUTING évolue
- Pour les connexion ODBC/JDBC Nous pouvions "router" un job dans un sous-système, par adresse Ip d'origine et depuis Navigator for i.
- le ADDPJE doit exister dans le sous-système indiqué
- Nous avons pu, ensuite, travailler pour un utilisateur et par procédure stockée :
- CALL SET_SERVER_SBS_ROUTING
- un select sur SERVER_SBS_ROUTING permet de faire la liste des utilisateurs concernés
- Pour enlever cette configuration, appelez la même procédure en passant la valeur nulle en 3ème paramètre
- Cette procédure accepte désormais plus de serveurs et peux router en fonction du profil ou de l'adresse IP
Paramètres :
- AUTHORIZATION_NAME : profil utilisateur (ou profil de groupe) ou *ALL (par IP)
- SERVEUR_NAME : serveur
voici la liste des serveur désormais disponibles
Server PGM Défaut Par utilisateur ? Par IP ? Central server QZSCSRVS QUSRWRK Oui Oui Database server QZDASOINIT QUSRWRK Oui Oui Data queue server QZHQSSRV QUSRWRK Oui Oui Db2 Mirror server (7.4) QDBMSRVR QUSRWRK Oui Oui DDM QRWTSRVR QUSRWRK Oui Oui DRDA QRWTSRVR QUSRWRK Oui Oui File server QPWFSERVSO QSERVER Oui Oui IBM i NetServer QZLSFILE QSERVER Non Oui Network print server QNPSERVS QUSRWRK Oui Oui Remote command server QZRCSRVS QUSRWRK Oui Oui Sign-on server QZSOSIGN QUSRWRK Non Oui - SUBSYSTEM_NAME : sous-système
- ALLOW_ROLLOVER : démarrer dans le sous-système par défaut en cas de prb ? (YES/NO)
- IP_ADDRESS_START : adresse IP ou adresse IP de début (plage) ou NULL
- IP_ADDRESS_END : adresse IP de fin ou NULL
- SUBNET_MASK : masque ou NULL
- PREFIX_LENGTH : IPV6
- SERVER_POSITION : position dans la table => ordre de recherche
- s'il existe déjà une autre ligne avec cette position, l'ancienne ligne est décalée de 1 vers le bas
- si NULL, on prend la plus grande valeur connue + 1
- REPLACEMENT_IP_ADDRESS_START : nouvelle adresse IP de début (si ce poste existe déjà)
- REPLACEMENT_IP_ADDRESS_END : nouvelle adresse IP de fin (si ce poste existe déjà)
- TEXT_DESCRIPTION : commentaire
- SERVER_SBS_ROUTING change pour refléter ces nouveaux serveurs
- l'affichage est toujours par Utilisateur
- nouvelles colonnes pour les nouveaux serveurs
- SERVER_SBS_CONFIGURATION (vue)
permet de voir l'ensemble des serveurs pour lequel on a fait un routage
- Par Navigator for i (SERVER_SEARCH_ORDER à NULL)
ou- SET_SERVER_SBS_ROUTING (SERVER_SEARCH_ORDER renseigné)
- la fonction table SYSTEM_STATUS change
- si vous avez le droit sur le *SRVPGM QSYS/QPMLPMGT, vous aurez en plus des informations LPAR
ACS en version 1.1.8.3
- ctrl+ espace lance un assistant à la saisie
- sur une table
- sur une liste de zones
- tous vos scripts peuvent être sauvegardés en tant qu'exemple
- Renseignez bien deux commentaires
- -- category
- -- description
DCM
nouvelle version de Digital Certficate Manager
Il faut installer les PTF SI71406 et toutes les PTF sécurité (SI71547, SI71373, SI71363, SI71361 and MF66742)
premiers tests :
- Le look est plus simple
- On ne retrouve pas tout
en ayant ouvert le certificat store *SYSTEM
Renouvellement d'un certificat
assignation à une application
Supression d'un certificat ayant expiré
si le certificat est encore associé à une application
RPG
De nombreuses nouveautés sont liées à ILE ILE ?
COMMENÇONS PAR CE QU'IL Y AVAIT AVANT :
L’environnement s’appelait OPM
la règle était simple
Un membre source → un programme
(CRTRPGPGM)Pour utiliser un autre code → CALL
ILE
On peut, pas on doit ;-)
Lier (linker)
des morceaux de code entre eux
Le module est l'unité de compilation
(CRTRPGMOD, par ex)
-> Pour linker CRTPGM
Le code (sourceC par ex.) est dupliqué !
En cas de correction/modification
il faut « relinker » tous les programmes
contenant sourceC !
(et nous sommes mal outillés pour cela) !
On peut aussi :
Ignorer cette notion et faire de l'ILE
comme M. Jourdain fait de la prose (sans le savoir)
CRTBNDRPG
ILE
Permet aussi de faire des procédures :
- comme un sous-programme,
c'est dans le même source-comme un programme,
les variables ne sont pas partagées (locales)
elles apparaissent dans la liste d'invocation
(DSPJOB / opt 11)Particulièrement en C et en RPG
Exemple : Un Sous/pgm (BEGSR) devient une procédure (DCL-PROC)
ENFIN, NOS RECOMMANDATIONS
Externaliser (et donc rendre unique) le code
par les programmes de services
ce qui correspond plus à notre culture à tous
source du *SRVPGM
les fonctions peuvent s'appeler les une les autres
(sans EXPORT la fonction est purement à usage interne)
MISE EN PLACE
Pour le programme de service
- CRTRPGMOD
- CRTSRVPGM EXPORT(*ALL)
Pour les programmes utilisant ce *SRVPGM
- CRTRPGMOD
- CRTPGM BNDSRVPGM(---) /* fastidieux ...*/
QUELQUES Conseils
Ne vous embêtez pas à indiquer les programmes de services utilisés par un programme
Utilisez les répertoires de liage
Créez un répertoire de liage « entreprise » (CRTBNDDIR votre-nom)
A chaque fois que vous créez un programme de service, enregistrez le (ADDBNDDIRE)
Compilez vos programmes avec BNDDIR :
ou mieux Ctl-opt BNDDIR(votre-nom) ; dans le source
Partez sur de bonnes bases, ne laissez pas le système générer des signatures
Utilisez le langage de liage
Créez un membre source par programme de service dans QSRVSRC
STRPGMEXP SIGNATURE('ma-signature-en-dur')
EXPORT symbol(routine1)
EXPORT symbol(routine2)
EXPORT symbol(routine3)
ENDPGMEXP
Si vous ajoutez une routine, ajoutez la en fin de liste :
STRPGMEXP SIGNATURE('ma-signature-en-dur')
EXPORT symbol(routine1)
EXPORT symbol(routine2)
EXPORT symbol(routine3)
EXPORT symbol(routine4)
ENDPGMEXP
NE CHANGEZ PAS LA SIGNATURE, sauf modification importante, vous seriez obligé de recompiler tous les programmes !
NE CHANGEZ PAS L'ORDRE, les programmes ne mémorisent pas qu'il doivent utiliser la routine qui se nomme « routine2 », mais la position de la routine (la n° 2)
- Si vous n'êtes pas parti sur ces bases :
DSPSRVPGM permet de voir la signature actuelle
Ecran 5, liste des procédures exportées
Ecran 9, signature générée correspondante
Vous pouvez alors retrouver le source de liage actuel par RTVBNDSRC et le modifier en conséquence
- Enfin, vous ne pouvez pas ignorer la notion de groupes d'activation
Utilisation
-
Prototype, permet de déclarer programme externe ou procédure (ILE)
DCL-PR QCMDEXC EXTPGM;
cde CHAR(50) CONST;
cdl PACKED(15 : 5) CONST;
END-PR;
- EXTPGM le nom du pgm externe (peut être qualifié)
si non précisé, il s'agit d'une procédure (au sens ILE), voir ci-dessous
- S'il s'agit d'une procédure de type fonction, indiquer le type retour sur la déclaration
DCL-PR DayOfWeek zoned(1); //retourne 1 à 7
datwrk DATE CONST;
END-PR;
- S'il n'y a pas de paramètre en entrée, indiquer END-PR sur la même ligne
DCL-PR MaFonction PACKED(5:0) END-PR;
- EXTPROC(*DCLCASE) pour imposer un respect absolu de la casse (Api systèmes, par ex)
- mots-clés liés aux paramètres
- CONST sur un paramètre indique que ce paramètre peut ne pas être une variable
(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 être 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]
- LIKEFILE
permet de passer un fichier déclaré et ouvert, en tant que paramètre, le fichier doit lui-même avoir été déclaré par LIKEFILE
(utilisation peu fréquente)
- CONST sur un paramètre indique que ce paramètre peut ne pas être une variable
- EXTPGM le nom du pgm externe (peut être qualifié)
Nouveautés TR1
- OPTION(*EXACT)
- renforce les contrôles entre ce qui est déclaré au niveau du prototype et ce qui est utilisé
- il faut que le paramètre soit de même longueur (avant il pouvait être plus grand)
- si c'est un paramètre LIKEDS, la DS modèle doit être la même
- OVERLAOD
- surcharge d'une fonction (choix d'une "implémentation" suivant les paramètres utilisés)
dcl-pr calechdate date;
inDate date CONST;
nbjour packed(3:0) CONST;
End-Pr;
dcl-pr calechnum8 date;
inDate packed(8:0) CONST;
nbjour packed(3:0) CONST;
End-Pr;
Dcl-pr calecheance date OVERLOAD(calechdate : calechnum8);
- DATA-GEN
nouveau code opération permettant la génération d'un flux structuré. Comme Data-Into (dont il s'inspire) il faut écrire le "driver"
- En résumé, voici la syntaxe
data-gen maDataStructure // données d'origine
%data('Exemple.txt' : 'doc=file ') // cible -> variable ou fichier
%gen('*LIBL/MONGENERATEUR') ; // handler
- Vous devez écrire le générateur ....
- un exemple est proposé pour générer un fichier "properties"
(dans QOAR/SAMPLE2, mais pour des raisons de CCSID copiez le dans un fichier à vous))
Entête
- Le générateur recoit une struture (de type QrnDgParm_T ) contenant
- generatorState, pointeur vers une information à mémoriser pour les appel suivants (facultatif, le type est libre)
- env, pointeur vers une DS contenant les pointeurs des procédures dites de "callback"
- handle, un pointeur à transmettre aux fonctions de callback
- userParm, second paramètre de la fonction %gen()
- userParmSize, taille de userParm
- userParmCcsid, ccsid de userParm
- userParmType, type du userParm
- outputIsToFile, booléen, vrai si doc=file
- isPartOfSequence, booléen
- on peut lancer une série (sequence) de Data-Gen, générant le même fichier en sortie
- il faut alors indiquer l'option output=continue
DATA-GEN *START %DATA('myfile.txt' : 'doc=file') %GEN('MYPGM');
DATA-GEN ds1 %DATA('myfile.txt' : 'doc=file output=continue') %GEN('MYPGM');
DATA-GEN ds2 %DATA('myfile.txt' : 'doc=file output=continue') %GEN('MYPGM');
DATA-GEN *END %DATA('myfile.txt' : 'doc=file') %GEN('MYPGM');- name, nom de la donnée à générer
- ds (de type QrnDgDS_T) à utiliser en cas d'événement startStruct(05) ou endStruct(06)
- array (de type QrnDgArray_T) à utiliser en cas d'événement startScalarArray (07) / startStructArray(09) ou endScalarArray(08) / endStructArray (10)
- scalar (de type QrnDgScalar_T) à utiliser en cas d'événement ScalarValue (11)
- Test de l'exemple fourni
- Nos premiers tests
- Résultat
- doc=file
- dsply output
- Le générateur (GEN01, procédure GenPropsFileOpts)
ctl-opt option(*srcstmt);
ctl-opt ccsid(*ucs2 : *utf16); // Code this for every generator
ctl-opt nomain;
/copy QOAR/QRPGLESRC,QRNDTAGEN // Copy file for all generators
/copy AF4TEST/MANIP740,GEN01_H // voir la capture d'écran ci-dessusSous procédures (parfaite illustration d'ILE)dcl-proc genPropsFileOpts export;
dcl-pi *n extproc(*dclcase);
parm likeds(QrnDgParm_t);
end-pi;dcl-ds userParm likeds(propGenOpts_t) based(pUserparm);
dcl-ds state qualified based(pState);
separator like(userParm.separator);
equalStartQuote like(userParm.quote : +1);
endQuote like(userParm.quote);
end-ds;pQrnDgEnv = parm.env; // fonctions callback
if parm.event = QrnDgEvent_01_StartMultiple // valeurs possibles
or parm.event = QrnDgEvent_03_Start;
// première fois ? if parm.generatorState = *null; // mémoire non allouée, première fois
// création de notre data structure "state" par allocation mémoire // afin de mémoriser les valeurs, soit recues, soit fixées par défaut
pState = %alloc(%size(state));
clear state;
state.separator = ';'; // séparateur par dft
parm.generatorState = pState;
endif;
endif; // fin de Start ou StartMultiple// accès à la DS state générée juste au-dessus
if parm.event = QrnDgEvent_03_Start;
// (le pointeur est transmis à chaque appel)
pState = parm.generatorState;
// s'il y a des options, vérification
if parm.userParm <> *null;
if parm.userParmType <> QrnUserParmType_dataStruct // voir les types
or parm.userParmSize <> %size(propGenOpts_t);
reportError (parm
: ERR_1_GEN_OPTIONS_NOT_VALID
: 'Options must be LIKEDS(propGenOpts_t)');
else; // options ok, récupération des paramètres
pUserParm = parm.userParm;
state.separator = userParm.separator;
state.equalStartQuote = '=' + userParm.quote;
state.endQuote = userParm.quote;
endif;
endif;
elseif parm.event = QrnDgEvent_11_ScalarValue;
// générer nom="valeur"// d'abord le nom
QrnDgAddText (parm.handle
: %addr(parm.name : *data)
: %len(parm.name));// le signe = suivi, éventuellement, par le délimiteur
QrnDgAddText (parm.handle
: %addr(state.equalStartQuote : *data)
: %len(state.equalStartQuote));// puis la valeur
QrnDgAddText (parm.handle
: parm.scalar.value
: parm.scalar.valueLenChars);// éventuellement, 2ème délimiteur
if %len(state.endQuote) > 0;
QrnDgAddText (parm.handle
: %addr(state.endQuote : *data)
: %len(state.endQuote));
endif;// sortie fichier : générer retour chariot
if parm.outputIsToFile;
QrnDgAddTextNewline (parm.handle);
else; // sinon caractère ";"
QrnDgAddText (parm.handle
: %addr(state.separator : *data)
: %len(state.separator));
endif;endif;
end-proc genPropsFileOpts;//--------------------------------------------------------------
// issueMessage - envoi message *DIAG ou *ESCAPE (erreur)
//--------------------------------------------------------------
dcl-proc issueMessage;
dcl-pi *n extproc(*dclcase);
msg varchar(200) const;
msgType char(10) const;
proc varchar(200) const;
stackOffset int(10) const;
end-pi;
dcl-pr QMHSNDPM extpgm;
msgId char(7) const;
msgFile likeds(qualMsgf);
msgData char(500) const;
dataLen int(10) const;
msgType char(10) const;
stackEntry char(10) const;
stackOffset int(10) const;
msgKey char(4) const;
errorCode likeds(errcode);
end-pr;
dcl-ds qualMsgf qualified;
msgf char(10) inz('QCPFMSG');
lib char(10) inz('*LIBL');
end-ds;
dcl-ds errCode qualified;
bytesProvided int(10) inz(0); // 0 = message d'erreur si prb.
bytesAvailable int(10) inz(0);
end-ds;
dcl-s msgkey char(4);
dcl-c STACK_OFFSET_TO_CALLER 2; // 1 pour le PEP(ILE) + 1 pour l'appelant
QMHSNDPM ('CPF9898' : qualMsgf
: msg : %len(msg) : msgType
: proc : stackOffset
: msgkey : errCode);
end-proc;//--------------------------------------------------------------
// reportError - fin de l'opération
//--------------------------------------------------------------
dcl-proc reportError;
dcl-pi *n extproc(*dclcase);
parm likeds(QrnDgParm_t);
errorCode int(10) value;
errorMessage varchar(500) const;
end-pi;// Envoi d'un message diag pour trace
issueMessage (errorMessage
// arrêt de l'opération Data-Gen
: DIAGNOSTIC_MESSAGE
: '*' : 2); // envoi à l'appelantQrnDgReportError (parm.handle
: errorCode);
end-proc;
- Du coup, nous avons fait notre propre exemple, générateur de CSV
Exemple d'utilisation (lecture de notre fichier vins)
**free ctl-opt option(*srcstmt) dftactgrp(*no); ctl-opt ccsid(*ucs2 : *utf16); // obligatoire /copy QOAR/QRPGLESRC,QRNDTAGEN // obligatoire dcl-pi *n ; parm likeds(QrnDgParm_t); end-pi; dcl-ds state qualified based(pState); premierefois IND; end-ds; // pour fonctions callback pQrnDgEnv = parm.env; if parm.event = QrnDgEvent_03_Start; if parm.generatorState = *null; // Allocattion mémoire pour "state"
// qui ne contient qu'un indicateur pState = %alloc(%size(state)); clear state; parm.generatorState = pState; endif; endif; // on retrouve state pState = parm.generatorState; if parm.event = QrnDgEvent_05_StartStruct; state.premierefois = *ON; EndIf; if parm.event = QrnDgEvent_11_ScalarValue; // une valeur if state.premierefois; state.premierefois = *OFF; else; // 2ème fois ou + // il y a une donnée devant ==> ; QrnDgAddTextString (parm.handle : ';'); endif;
if parm.scalar.datatype = QrnDatatype_Alpha or parm.scalar.datatype = QrnDatatype_AlphaVarying; // CHAR ==> " devant et derrière QrnDgAddTextString (parm.handle : '"'); EndIf;
// la valeur QrnDgAddText (parm.handle : parm.scalar.value : parm.scalar.valueLenChars); if parm.scalar.datatype = QrnDatatype_Alpha or parm.scalar.datatype = QrnDatatype_AlphaVarying; // CHAR ==> " devant et derrière QrnDgAddTextString (parm.handle : '"'); EndIf; endif; if parm.event = QrnDgEvent_06_EndStruct; // fin de srtucture => retour chariot if parm.outputIsToFile; QrnDgAddTextNewline (parm.handle); endif; endif; return;
SI vous voulez une trace
**free ctl-opt option(*srcstmt) alwnull(*usrctl); dcl-f vins disk ALIAS; dcl-ds vinds likerec(vinsf) ; data-gen *START %data('testCsv.txt' : 'doc=file') %gen('GENCSV'); read vins vinds; dow not %eof; data-gen vinds %data('testCsv.txt' : 'doc=file output=continue') %gen('GENCSV'); read vins vinds; enddo; data-gen *END %data('testCsv.txt' : 'doc=file') %gen('GENCSV'); *inLR = *ON;
- ADDENVVAR QIBM_RPG_DATA_GEN_TRACE VALUE('*STDOUT')
- pour afficher STDOUT, voyez ce pgm donné par IBM
CTL-OPT ACTGRP(*NEW) BNDDIR('QC2LE'); DCL-PR printf EXTPROC(*DCLCASE); p POINTER VALUE OPTIONS(*STRING : *NOPASS); END-PR; DCL-PR getchar INT(10) EXTPROC(*DCLCASE) END-PR; DCL-C EOL x'15'; printf (EOL); getchar (); return;
- In Extremis : RDI 9.6.0.7
-
Nouvelle fenêtre liste de bibliothèques
Nouvelle option de refactoring, transformez quelques lignes de code en procédure
-