https://www.gaia.fr/wp-content/uploads/2017/02/team3.png600600Pierre-Louis BERTHOIN/wp-content/uploads/2017/05/logogaia.pngPierre-Louis BERTHOIN2025-08-19 16:40:022025-08-19 16:40:03Débuter en java sur ibmi
Vous avez un nouveau paramètre sur les commandes CRTBNDRPG ou CRTRPGMOD DATEYY( )
DATE WITH 2-DIGIT YEARS . . . . DATEYY( *ALLOW)
Ce paramètre n’est pas encore documenté dans l’aide mais vous comprenez , que c’est pour les dates sur 6 caractères
Rappel sur les dates à 6 , vous avez un point de bascule: 40 – 99 : Le siècle est supposé être « 19 » 00 – 39 : Le siècle est supposé être « 20 »
C’est dans 14 ans
Pour ce paramètre, DATEYY vous avez 3 valeurs possibles
DATEYY(*ALLOW) : autorise tous les formats de date, autrement dit, n’effectue aucune validation. DATEYY(WARN) : si une date est détectée, elle est considérée comme une année sur deux caractères, une erreur de compilation de niveau 10 est générée. DATEYY(*NOALLOW) : si une date pourrait être détectée, la compilation est renvoyée avec une erreur de niveau 30. La valeur par défaut est « *ALLOW ».
il est conseillé de compiler avec *WARM, vous aurez une liste des problèmes potentiels
Ce message apparaitra
Msg id Sv Number Seq Message text *RNF0201 10 5 002300 WARNING: A DATE WITH 2 DIGITS FOR THE YEAR ONLY SUPPORTS THE YEARS 1940 TO 2039. REASON CODE: xxxxxx.
Rappel: Pensez dans SQL à bien utiliser des formats sur 8 *ISO par exemple pour tous vos calculs
https://www.gaia.fr/wp-content/uploads/2017/02/team3.png600600Pierre-Louis BERTHOIN/wp-content/uploads/2017/05/logogaia.pngPierre-Louis BERTHOIN2025-08-18 16:42:512025-08-20 11:32:37V7R6 , date sur 6 positions en RPG
Vous voulez changer le groupe d’activation d’un programme. Contrairement à une idée reçue, on peut dans certains cas renommer le groupe d’activation d’un programme ILE
Si vous créez un programme
CRTPGM
Ou
CRTBNDRPG
Vous allez indiquer le groupe d’activation d’exécution de votre programme
Vous ne pouvez pas changer le groupe d’activation par CHGPGM !
Mais vous pouvez le faire par la commande UPDPGM
avant ==> DSPPGM AATSTRET
Attribut du groupe d’activation . . . . . . . : PLB1
UPDPGM PGM(AATSTRET) MODULE(*NONE) ACTGRP(PLB45)
Valeurs des paramètres AUT et USRPRF ignorées.
L’objet remplacé AATSTRET type *PGM a été déplacé dans QRPLOBJ.
Programme AATSTRET créé dans la bibliothèque GDATA.
Programme AATSTRET mis à jour dans GDATA.
après ==>DSPPGM AATSTRET
Attribut du groupe d’activation . . . . . . . : PLB45
la seule limitation est que le groupe doit être nommé
Pour interdir ce changement à l’assemblage vous devez indiquer
CRTPGM … ALWUPD(*NO)
PS :
Cette option n’existe pas sur le CRTBNDRPG donc modifiable par défaut
Vous devez avoir le droit *change sur programme.
Rappel:
En batch on essaye d’avoir le premier programme qui crée le groupe d’activation et les programmes appelés s’exécuteront en *caller
Dans les autres cas, webservice, interactif, etc il peut être préférable d’avoir un groupe d’activation par programme
Pour analyser les groupes actifs, vous pouvez utiliser le service : QSYS2.ACTIVATION_GROUP_INFO
— Liste des groupes par Travail
SELECT A.JOB_NAME,
count(*)
FROM TABLE (
QSYS2.ACTIVE_JOB_INFO()
) AS A
LEFT JOIN TABLE (
QSYS2.ACTIVATION_GROUP_INFO(JOB_NAME => A.JOB_NAME)
) AS G
ON 1 = 1
group by A.JOB_NAME
ORDER BY count(*) desc
;
https://www.gaia.fr/wp-content/uploads/2017/02/team3.png600600Pierre-Louis BERTHOIN/wp-content/uploads/2017/05/logogaia.pngPierre-Louis BERTHOIN2025-08-13 11:29:572025-08-19 13:54:43Changer le groupe d’activation d’un programme
REXX (Restructured Extended Executor) est un langage de script interprété créé par IBM, bien connu pour les « Roger » qui ont sévit sous OS2. Il est conçu pour être facile à lire et facile à apprendre, tout en étant très puissant pour l’automatisation.
Sur IBMi, il est utilisé pour : Automatiser des tâches système Créer des utilitaires interactifs Prototyper rapidement Faire du traitement de texte et de données
Ces points forts sont : Très rapide à écrire, idéal pour du scripting jetable Permet d’appeler directement des commandes système sans compiler un programme Peut servir de « colle » entre RPG, CL, SQL et PASE/QShell Permet de faire des tests d’appels, des scripts de migration, des reprises de données.
Comment ca marche?
Vous devez créer un fichier source qui contiendra les scripts à exécuter
/* REXX / / Boucle interactive jusqu’à ce que l’utilisateur tape ‘FIN’ */
DO FOREVER SAY « Entrez une commande CL (ou FIN pour quitter) : » PULL CMD IF CMD = « FIN » THEN LEAVE ADDRESS ‘COMMAND’ CMD END
Ce scripte exécutera des commandes CLP, jusqu’à ce que saisissiez FIN
Pour exécuter ce script :
STRREXPRC SRCMBR(REXX01) SRCFILE(MALIB/QRXSRC)
Remarque : Le rexx est de moins en moins utilisé mais, il peut encore être utilisé, en effet, il peut aider a du déploiement et de la mise au point, etc…
En bons élèves, nous profitons de l’été pour faire nos devoirs de vacances. Entre autre, installation de la version 7.6 sur plusieurs de nos partitions.
D’abord une partition de test, bac à sable, puis nos partitions de production i(vous avez peut être vu une interruption de service sur nos sites web).
J’en profite pour faire un petit retour d’expérience sur ces installations.
Et pour finir ce préambule, je remercie l’équipe du support !
Une mise à jour comme les autres ?
Oui, je ne détaille pas ici le processus de mise à jour, c’est classique : téléchargement des images et clés sur ESS, PRUV pour les contrôle etc …
Quelques points d’attention
DST/SST
Le mot de passe de QSECOFR ne doit pas être celui par défaut (sinon il sera inutilisable)
Les profils 11111111 et 22222222 sont supprimés pendant l’installation
Bonne pratique : créer au moins un autre profil avec tous les droits pour DST, changer le mot de passe de QSECOFR. Les profils 11111111 et 22222222 ne devraient déjà plus être utilisés depuis longtemps
Java 11 64 bit non supporté
Anticiper les changement de configuration si vous l’utilisez
Impossible de saisir le mot de passe de QSECOFR au démarrage
Après l’installation de la 7.6 sur la partition, et fort satisfait, je tentes de me loguer avec QSECOFR : informations d’authentification incorrectes !
Après vérification dans mon gestionnaire de mots de passe, nouvelle tentative : idem.
A la 3ème, profil désactivé …
Heureusement, j’ai toujours un profil clone de QSECOFR pour remédier à ces situations : je change le mot de passe de QSECOFR, et là cela fonctionne !
Bonne pratique : changer le mot de passe de QSECOFR avant la migration pour un mot de passe simple (quitte à déroger aux règles le temps de l’installation).
Problème de connexion sur tous les serveurs hôtes en TLS
Après installation de la 7.6, impossible de se connecter en telnet sécurisé, ni aux autres services
Dans DCM, allez dans le magasin *SYSTEM, puis gérer « Manage Applications Definitions », sélectionner l’application QIBM_QZBS_SVR_HOSTCNN et associez un certificat (en général le même pour l’ensemble des serveurs hôtes) :
Dès validation la connexion TLS fonctionne, pas besoin d’arrêter/redémarrer des services.
Ce fonctionnement est nécessaire pour la mise en œuvre du MFA, mais impact les services existants.
Tous les systèmes étant sécurisés depuis de nombreuses années, vous devriez être impacté …
En synthèse
A noter que l’ensemble des éléments est bien documenté, y compris les « détails » qui nous ont posés quelques soucis (exception du mot de passe non reconnu).
Une bonne préparation permet de réaliser une migration maitrisée, l’installation elle-même se déroule très bien et rapidement.
https://www.gaia.fr/wp-content/uploads/2017/02/team1.png600600Nathanaël Bonnet/wp-content/uploads/2017/05/logogaia.pngNathanaël Bonnet2025-08-05 09:55:002025-07-30 17:59:29Retour d’expérience installation IBM i 7.6
https://www.gaia.fr/wp-content/uploads/2017/02/team3.png600600Pierre-Louis BERTHOIN/wp-content/uploads/2017/05/logogaia.pngPierre-Louis BERTHOIN2025-08-04 10:53:552025-08-04 10:53:56V7R6, la liste des commandes supprimées
Je vais essayer de vous expliquer le routage des travaux interactifs.
Un travail interactif est routé vers un sous système par rapport au nom de son unité.
Vous l’indiquez dans la commande ADDWSE Vous avez 2 paramètres WRKSTN() nom écran ou nom générique WRKSTNTYPE type d’écran
l’ordre c’est nom nom générique type
Par contre ce n’est pas dynamique un écran affecté ne sera pas réaffecté dynamiquement, si par exemple votre sous système spécifique est démarré après QINTER.
Il peut être important pour des questions d’administration d’avoir les travaux dans le bon système.
On peut donc ajouter un programme SDA (sélection dynamique à l’arrivée), parce que le critère de nom n’est pas forcément suffisant , ou que suite à un redémarrage, on veut replacer le travail dans le bon sous système, ou tout autre raison.
C’est dans les données de routage qu’il va falloir agir, par défaut PGM(QCMD), mais vous pouvez indiquer un programme à vous. Ce programme ne reçoit pas de paramètre extérieur et il va par rapport à un algorithme que vous aurez déterminé transférer le travail vers une JOBQ, c’est la commande TFRJOB qui permet de transférer Le plus souvent on utilise comme critère l’utilisateur ou le nom de l’unité, mais on peut ajouter beaucoup d’autres critéres
Voici un exemple très simple et relativement classique, à mettre sur QINTER qui est souvent le sous système par défaut puisqu’il a une entrée par type
/*------------------------------------------------------------------*/
/* Ce programme est utilisé dans un sous système interactif */
/* dans les données de routage il va rerouter une jobq */
/* ADDRTGE ... PGM(MALIB/REROOT) */
/* Ce programme doit être en adoption de droit par rapport QSECOFR */
/*------------------------------------------------------------------*/
PGM
dcl &user *char 10 /* Utilisateur en cours */
dcl &grp *char 10 /* Groupe de l'utilisateur */
dcl &unit *char 10 /* nom de l'unité */
dcl &status *char 2
/* information pour adresse ip */
dcl var(&IPV4ADDR) type(*char) len(15)
DCL VAR(&JOBI) TYPE(*CHAR) LEN(700) VALUE(' ')
DCL VAR(&JOBILEN) TYPE(*DEC) LEN(4 0) VALUE(700)
DCL VAR(&FMTNAME) TYPE(*CHAR) LEN(8) VALUE(JOBI0600)
DCL VAR(&QUALJN) TYPE(*CHAR) LEN(26) VALUE('*')
DCL VAR(&INJOB) TYPE(*CHAR) LEN(16) VALUE(' ')
/*-------------------------------------------------*/
/* Extraction des informations de routage */
/* ici Utilisateur, groupe et nom unité */
/*-------------------------------------------------*/
RTVJOBA USER(&USER) job(&unit)
RTVUSRPRF GRPPRF(&GRP)
/* Call QUSRJOBI to retrieve job information */
CALL PGM(QSYS/QUSRJOBI) PARM(&JOBI &JOBILEN &FMTNAME +
&QUALJN &INJOB)
/* Extract data from the output of QUSRJOBI */
CHGVAR VAR(&IPV4ADDR) VALUE(%SST(&JOBI 308 15))
/*-------------------------------------------------*/
/* Règles de routage sur utilisateur et groupe */
/*-------------------------------------------------*/
if cond(&GRP = 'COMPTA') then(do)
TFRJOB JOBQ(COMPTA)
enddo
/*-------------------------------------------------*/
/* Règles de routage sur unité */
/* reroutage de l'unité de secours dans QCTL */
/* Pour faire des operations en mode restreint */
/*-------------------------------------------------*/
if cond(&UNIT = 'SECOURS') then(do)
TFRJOB JOBQ(QCTL)
enddo
/*-------------------------------------------------*/
/* Règles de routage sur nom d'ecran */
/* AAAAAAxxxxx vers JOBQ AAAAAAJOBQ */
/* AAAAAA racine de l'ecran */
/* xxxx numéro */
/*-------------------------------------------------*/
chgvar &status 'OK'
CHKOBJ OBJ((%SST(&UNIT 1 6) *TCAT 'JOBQ')) +
OBJTYPE(*JOBQ)
monmsg cpf9800 exec(do)
chgvar &status 'KO'
enddo
/* si JOBQ existe */
if cond(&status = 'OK') then(do)
TFRJOB JOBQ((%SST(&UNIT 1 6) *TCAT 'JOBQ'))
enddo
/*-------------------------------------------------*/
/* Règles de routage sur adresse ip */
/* Ici controle adresse ip avec refus si pas reseau*/
/* 172 */
/*-------------------------------------------------*/
if cond(%sst(&IPV4ADDR 1 3) *ne '172') +
then(do)
SNDMSG MSG('Tentative de connexion refusée,' *BCAT +
&IPV4ADDR *BCAT ', sur adresse IP' *BCAT +
&UNIT) TOMSGQ(*HSTLOG)
ENDJOB JOB(*)
enddo
/*-------------------------------------------------*/
/* Les autres ne sont pas reroutés */
/*-------------------------------------------------*/
endpgm
Dans notre exemple, on a ajouté un contrôle sur l’adresse IP avec un blocage si pas le réseau attendu
Remarques :
La limite c’est votre imagination, vous devez bien réfléchir et cibler votre besoin , vous pouvez par exemple ajouter du paramétrage
Vous pouvez également utiliser le programme d’exit (QIBM_QTG_DEVINIT) , mais plus compliqué à mettre en œuvre
Vous devez vous assurer que le programme utilisé pour SDA ne plante pas
Les unités peuvent être créer automatiquement c’est la valeur système QAUTOVRT
Si vous choisissez de l’utiliser, préférez un nombre (exemple : 500 ) plutôt que *NOMAX et tous les 6 mois faites un coup de ménage sur les unités inutilisées
===> DSPOBJD OBJ(ALL) OBJTYPE(DEVD)
Vous pouvez les identifier facilement :
Par défaut les unités s’appellent QPADEVxxxx , ne pas hésiter à les supprimer 2 fois par an
https://www.gaia.fr/wp-content/uploads/2017/02/team3.png600600Pierre-Louis BERTHOIN/wp-content/uploads/2017/05/logogaia.pngPierre-Louis BERTHOIN2025-07-23 14:06:242025-07-27 11:28:07Routage des travaux interactifs
Vous avez une instruction SQL , MERGE qui est assez PUISSANTE pour fusionner des fichiers Voici un exemple complet :
Il intègre les éléments suivants -La création -La mise à jour -La suppression
On a 2 tables Products et Mouvements
Les règles choisies sont les suivantes :
-Si le produit existe on ajoute la quantité -S’ il est nouveau, on le créé -Si nom du produit est SUPPRESSION, on supprime
J’ai utilisé la convention de nommage *SYS
et dans tous les cas on met à jour la date de modification
-- voici les scripts pour tester
-- Création de la table des produits
-- Option *SYS et *NONE
CREATE TABLE GDATA/PRODUCTS (
PRODUCT_NUMBER DECIMAL(10, 0) NOT NULL ,
PRODUCT_NAME VARCHAR(100) NOT NULL,
QUANTITY DECIMAL(10, 0) NOT NULL DEFAULT 0,
LAST_UPDATE_TS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (PRODUCT_NUMBER)
);
-- alimentation du fichier
INSERT INTO GDATA/PRODUCTS VALUES(1, 'CLOU', 50, current timestamp) ;
INSERT INTO GDATA/PRODUCTS VALUES(2, 'VIS', 20, current timestamp) ;
INSERT INTO GDATA/PRODUCTS VALUES(3, 'ECROU', 25, current timestamp) ;
INSERT INTO GDATA/PRODUCTS VALUES(4, 'RONDELLE', 120, current timestamp) ;
-- Création de la table des mouvements
CREATE TABLE GDATA/MOUVEMENTS (
PRODUCT_NUMBER DECIMAL(10, 0) NOT NULL ,
PRODUCT_NAME VARCHAR(100) NOT NULL,
QUANTITY DECIMAL(10, 0) NOT NULL DEFAULT 0,
LAST_UPDATE_TS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (PRODUCT_NUMBER)
);
-- alimentation de la tables des mouvements
INSERT INTO GDATA/MOUVEMENTS VALUES(1, 'CLOU', 30, current timestamp) ; -- changement de quantité
INSERT INTO GDATA/MOUVEMENTS VALUES(3, 'SUPPRESSION', 0, current timestamp) ; -- suppression
INSERT INTO GDATA/MOUVEMENTS VALUES(5, 'RESSORT', 100, current timestamp); -- Nouveau
-- Fusion des 2 tables
MERGE INTO GDATA/PRODUCTS AS T -- T est l'alias de la table CIBLE (PRODUCTS)
USING GDATA/MOUVEMENTS AS S -- S est l'alias de la table SOURCE (MOUVEMENT)
ON (T.PRODUCT_NAME = S.PRODUCT_NAME) -- La jointure se fait sur le nom du produit
-- 1. Gérer la suppression si le produit correspond ET que la source indique 'SUPPRESSION'
WHEN MATCHED AND S.PRODUCT_NAME = 'SUPPRESSION' THEN
DELETE
-- 2. Gérer la mise à jour si le produit correspond ET que la source N'indique PAS 'SUPPRESSION'
WHEN MATCHED AND S.PRODUCT_NAME <> 'SUPPRESSION' THEN
UPDATE SET T.QUANTITY = T.QUANTITY + S.QUANTITY,
T.LAST_UPDATE_TS = CURRENT_TIMESTAMP
-- 3. Gérer l'insertion si le produit NE correspond PAS ET que la source N'indique PAS 'SUPPRESSION'
WHEN NOT MATCHED AND S.PRODUCT_NAME <> 'SUPPRESSION' THEN
INSERT (PRODUCT_NUMBER, PRODUCT_NAME, QUANTITY, LAST_UPDATE_TS)
VALUES (S.PRODUCT_NUMBER, S.PRODUCT_NAME, S.QUANTITY, CURRENT_TIMESTAMP);
Remarque :
Vous pouvez également utiliser la commande CPYF avec le paramètre MBROPT(*UPDADD) mais plus compliqué de gérer les suppressions.
Access Client Solutions 1.1.9.8, disponible depuis avril 2025, amène son lot d’évolutions. Une m’a particulièrement intéressée : l’affichage des index considérés.
Visual Explain, les index ?
Visual Explain permet d’afficher le plan d’exécution de la requête SQL : l’ensemble des étapes nécessaires à l’obtention du résultat, de la façon la plus optimisée possible.
Pour déterminer la façon la plus optimisée, le moteur SQL va réécrire la requête, considérer les index/LF existants, exploiter les statistiques de chaque table, index ou clé sous-jacents aux tables utilisées dans la requête.
Dans le plan affiché, pour une première analyse macroscopique, on cherche en général les éléments suivants :
Scan de table : on préfère utiliser des accès par index plutôt que parcourir l’ensemble de la table. Il s’agit d’analyser pourquoi aucun index ne satisfait les conditions de la requête
Les index utilisés : même si l’on se félicite de l’utilisation d’index, il est souvent possible de faire mieux
Les index recommandés : justement pour faire mieux !
Une information est disponible mais difficilement exploitable : l’optimiseur explique pour chaque index trouvé pourquoi il a été utilisé, ou pourquoi il ne l’a pas été.
Un exemple
Nous avons une table dans laquelle nous consolidons certains événements logués par nos serveurs web (access_log générés par Apache). Aujourd’hui cette table HTTPLOG contient environ 240 millions d’entrées, et dispose bien évidemment d’un certains nombres d’index existants :
Prenons une requête basique :
L’affichage de Visual Explain nous montre :
En sélectionnant l’étape « Test de table », le volet de droite indique :
Ce sont la liste des index que l’optimiseur a regardé et le chiffre correspond au code qui indique pourquoi il n’a pas été utilisé. Il est possible d’aller chercher le détail des codes dans les messages (il faut activer les messages de débogage).
Cette information est accessible pour chaque étape du plan, dans le cas de jointure ou de sous-requête.
Index considérés
Prenons une autre requête SQL permettant d’analyser toutes les requêtes HTTP authentifiées et dont le retour provoque un warning ou une erreur (autre que code HTTP 200 OK).
Visual Explain nous donne :
Nous retrouvons bien entendu nos informations sur les index :
Mais il est maintenant possible de demander ces informations pour l’ensemble de la requête :
Et d’obtenir des libellés plus parlants :
Cela vous donne plus d’informations quant à l’usage des index. Pour compléter, l’index advisor nous donne :
A noter que l’optimiseur ne propose pas d’index dérivé par exemple, ou difficilement les index EVI … Gardez donc un œil critique sur ces informations, mais leur compréhension est nécessaire.