Avec la TR6 de la V7R4 (TR disponible à partir du 24 mai 2022)


Vous pouvez désormais compresser en standard avec l’algorithme *ZLIB dans les commandes SAV*


Bien que un peu vieux, (la première version date de 1995) c’est un algo très fiable et efficace


Pour en savoir plus sur ZLIB https://fr.wikipedia.org/wiki/Zlib

Remarque :

Jusque la on pouvait l’installer en open-source

Envoyer un message avec réponse

Il peut être intéressant d’envoyer un message avec une réponse, pour des traitements occasionnels par exemple .

Voici 2 exemples de code qui font la même chose

exemple en RPG

**free
// Message simple avec réponse
// texte 55 max
dcl-s w_reponse char(1) ;
dsply ‘Voulez-vous continuer ?’ ‘QSYSOPR’ w_reponse;
// traitement de la réponse
if w_reponse = ‘O’;
dsply ‘Oui avec plaisir !’ ‘QSYSOPR’;
else ;
dsply ‘Certainement pas !’ ‘QSYSOPR’ ;
endif ;
*inlr = *on ;

exemple en répondant O

Exemple en CLP

pgm
/* Message simple avec réponse / / */
dcl &w_reponse *char 10
SNDUSRMSG MSG(‘Voulez-vous continuer ?’) +
TOMSGQ(QSYSOPR) MSGRPY(&W_REPONSE)
if cond(&w_reponse = ‘O’) then(do)
SNDUSRMSG MSG(‘Oui avec plaisir !’) msgtype(info) TOMSGQ(QSYSOPR)

enddo

else do

SNDUSRMSG MSG(‘Certainement pas !’) msgtype(info) TOMSGQ(QSYSOPR)
enddo
endpgm

exemple en répondant O

Rappel

C’est n’est pas la meilleur manière pour traiter de l’information, mais ca peut rendre service

, Réorganiser un très gros fichier

Voici la problématique qui nous est arrivée :

Suite à un gros coup de ménage sur une petite machine saturée avec un énorme fichier de 12 millions d’enregistrement plus 30 millions d’enregistrements supprimés et 4 index dessus

Le réflexe naturel, a été le bon vieux RGZPFM qui devait nous réorganiser tout ça … eh bien non. On n’a jamais réussi à le lancer jusqu’au bout.

Voici comment nous avons fait pour nous en sortir. Nous avons utilisé une des possibilités du CPYF qui élimine les enregistrements supprimés.

En gros voici les opérations à réaliser


1) Copie vers un fichier de sauvegarde de votre fichier
2) Suppression des index pour éviter de saturer le système en reconstruisant tous les index en même temps
3) Mise à blanc du fichier initial
4) Recopie des données depuis votre sauvegarde
5) Remise des index initiaux

Vous pouvez retrouver un exemple de source qui fait ça ici https://github.com/Plberthoin/PLB/tree/master/GTOOLS

Remarque :


Dans cet outil l’astuce utilisée pour la suppression des index ne marche que sur les LF et pas sur les index (le plus simple dans ce cas la est d’extraire le source , droper l’index et rejouer votre script SQL)
On doit pouvoir également faire une copie par insert into … select
Il existe certainement d’autres alternatives …

, Génération CSV à partir de SQL

Il existe plusieurs méthodes pour faire du CSV, la solution la plus connue est la commande CPYTOIMPF.

Les procédures SQL peuvent offrir une alternative intéressante dans certains cas, voici un exemple.

Cette exemple utilise « Dynamic Compound Statement » avec la procédure QSYS2.IFS_WRITE_UTF8

quauoopt est le fichier des options PDM

BEGIN
— Génération fichier + entête
CALL QSYS2.IFS_WRITE_UTF8(PATH_NAME =>’/tmp/qauoopt.csv’,
LINE => ‘Option;Commande’,
OVERWRITE => ‘REPLACE’,
END_OF_LINE => ‘NONE’);
— Boucle de traitement des lignes
FOR SELECT option concat ‘;’ concat command as TEXTE From QGPL.QUAUOOPT DO
CALL QSYS2.IFS_WRITE_UTF8(PATH_NAME => ‘/tmp/qauoopt.csv’,
LINE => TEXTE);
END FOR;
END

Remarque :

Cette solution ne peut pas s’appliquer partout , en effet un « ; » dans une zone pourrait poser un problème à la lecture.
A l’inverse vous pouvez ajouter des instructions comme dans la fichiers XLS par exemple.

Il y a plusieurs procédures qui vous permettent de gérer l’IFS, et ca nous simplifie la tache.

pour retrouver les informations sur SQL services, https://www.ibm.com/support/pages/ibm-i-services-sql

, Extraire simplement vos références Web Services <-> Programme (de service)

Nous développons de plus en plus de web services grâce à IWS (Integrated Web Services).

L’implémentation de ces services peut être un programme, un programme de service, ou SQL. Par ailleurs, les deux dernières solutions nous permettent d’avoir des services avec plusieurs opérations (vocabulaire SOAP) ou routes (vocabulaire REST). Quoiqu’il en soit, plusieurs actions possibles au travers d’un unique service.

La question se pose désormais à plus grande échelle des impacts sur la maintenance des programmes et programmes de service sous-jacents à des services web !

Concrètement : je modifie un programme (de service) : comment savoir s’il est exposé en tant que service web ?

Scripts fournis

IWS est fourni avec des scripts, dans le répertoire /QIBM/ProdData/OS/WebServices/bin

Ces scripts permettent d’automatiser toutes les actions possibles, autrement disponibles via l’interface d’administration :

Nous vous recommandons leur usage pour déployer vos services par exemple, de façon automatique avec vos outils.

Ces scripts sont également capables d’extraire des informations des serveurs et services !

Extraction

Pour le principe ici, un script shell qui utilise ces fonctionnalités pour lister tous les services et récupérer tous les programmes (de service) référencés ! Le résultat est stocké en BD. C’est perfectible, mais démontre le fonctionnement :

#
# Extraire les références croisées web service <-> programme/programme de service
#

# Paramètres : bibliothèque de création du fichier 

export PATH=$PATH:/QIBM/ProdData/OS/WebServices/bin

# créer le fichier BD pour stocker les liens
db2 "create or replace table $1.wsxref (server varchar(10), service varchar(128), program varchar(128)) on replace delete rows"

# Lister les serveurs sans tenir compte de l'état (started/stopped)
servers=$(listWebServicesServers.sh | sed 's/(.*//' )
# Pour chaque serveur listé
for server in $servers ; do
  # Retourver les services sans tenir compte de l'état
  services=$(listWebServices.sh -server $server | sed 's/(.*//')
  # Pour chaque service
  for service in $services ; do
#   # Retrouver les propriétés. Ne garder que l'implémentation
	program=$(getWebServiceProperties.sh -server $server -service $service | grep -i 'Program object path:' | sed 's/Program object path://')
	echo $server $service $program
    db2 "insert into $1.wsxref values('$server', '$service', trim('$program'))"
  done
done

Le code est disponible ici : https://github.com/FrenchIBMi/webservices/blob/master/wsxref.sh

Une fois le script lancé (prévoyez un café le temps de l’exécution) :

Et voilà !

Vous pouvez l’appeler dans QSH, le planifier (commande QSH …), le mettre dans un CL. Et surtout intégrer le résultat dans vos outils d’analyse !

, , , Préciser la bibliothèque dans un SELECT INTO

Vous avez dans un programme RPGLE un select into en SQL à faire, mais vous devez choisir la bibliothèque ou se trouve le fichier !

Voici 3 solutions pour éviter le hard codage

1ére solution

Cette solution est plus adaptée à un curseur.

Formater une variable exemple sqlstm

sqlstm = ‘select votrezone from ‘ + votre_lib + ‘/votre_table’ ;

exec SQL
declare curs01 Cursor
for sqlstm ;

exec SQL
prepare sqlstm
from :sqlstm ;

exec SQL
open curs01 ;

exec sql
fetch next
from curs01
into :votrezone ;

On est d’accord, c’est un peu lourd

Voici une alternative

Si vous êtes en convention de nommage *SYS, vous pouvez utiliser la liste de bibliothèques

exec sql
call qcmdexc( ‘addlible ‘ concat :votre_lib) ;
// select sans bibliothèque
exec sql
select mazone into : wmazone from matable ;

Si vous êtes en convention de nommage *SQL, vous devez préciser le schéma courant – par défaut c’est le profil utilisateur.
// select sans bibliothèque
exec sql
set schema :votre_lib ;

exec sql
select mazone into : wmazone from matable ;

A noter que dans la cas d’une convention de nommage *SQL, toutes vos tables doivent être dans la même bibliothèque, en cas de jointure par exemple

Il existe effectivement une autre alternative avec dynamic SQL,

Merci Birgitta pour cette précision .

Avec dynamic SQL et VALUES … INTO:
Exemple:
CmdSQL = ‘Values( Select Count(*) from ‘ + %Trim(YourSchema) + ‘.YourTable ‘ +
‘ ) into ?’;

Exec SQL Prepare DynSQL from :CmdSQL;
Exec SQL Execute DynSQL using :YourResult;

, , Zones numériques étendues

Voici une petite expérience à faire sur les zones étendues

Commençons par créer un fichier PF comme ceci

A R TESTAF
A NUMERO 5
A NOM 30
A PRENOM 30

Compilez-le

on lui met des données

par exemple par SQL

INSERT INTO GDATA/TESTA VALUES(‘AAAAA’, ‘Carlsen’, ‘Magnus’)
INSERT INTO GDATA/TESTA VALUES(‘BBBBB’, ‘Vachier-Lagrave’, ‘Maxime’)
INSERT INTO GDATA/TESTA VALUES(‘CCCCC’, ‘Firouzja’, ‘Alireza’)

Si on fait un DSPPFM, on voit ceci

On décide de changer NUMERO et de le passer en numérique

On a donc le fichier décrit comme suit

A R TESTNF
A NUMERO 5S 0
A NOM 30
A PRENOM 30

Vous le compilez

vous décidez de récupérer les données

par un CPYF

CPYF FROMFILE(TESTA)
TOFILE(TESTN)
MBROPT(*REPLACE) FMTOPT(*NOCHK)

vous faites DSPPFM du fichier TESTN, voici le résultat :

Vous faites un select * from TESTN voici le résultat :

C’est étonnant non ?

Si vous faites un contrôle de données vous n’avez pas d’erreur

SELECT * FROM TABLE(SYSTOOLS.VALIDATE_DATA_FILE(‘GDATA’, ‘TESTN’));

Vous allez me dire pourquoi ne pas faire le nouveau fichier comme une table, allons y ?

CREATE TABLE GDATA.TESTS (
NUMERO NUMERIC(5, 0) NOT NULL DEFAULT 0 ,
NOM CHAR(30) CCSID 1147 NOT NULL DEFAULT  » ,
PRENOM CHAR(30) CCSID 1147 NOT NULL DEFAULT  » )
RCDFMT TESTS

On récupère a nouveau des données

CPYF FROMFILE(TESTA)
TOFILE(TESTN)
MBROPT(*REPLACE) FMTOPT(*NOCHK)

Si on fait un DSPPFM voici le résultat :

Si on fait un select * from TESTS :

conclusion

Si vous copiez des données alpha dans du numérique étendu

1/ En DDS

Les données sont fausses dans le fichier et interprétées à l’exécution SQL
attention si vous lisez en RPG les données ne sont pas chargées et il n’y a pas d’erreur !

exemple :

fTESTN if e disk
read(e) TESTN ;
if %error ;
dsply ‘ici’ ;
else ;
dsply ‘la’ ;
dsply NOM ; ;
endif ;
*inlr = *on ;

2/ En SQL

Le données sont converties lors du CPYF, mais elles ne sont pas bonnes : on passe ici de AAAAA à 11111

Faites attention si vous récupérez des données numériques étendues vous pourriez avoir des surprises

Dernière remarque

Il n’y pas de problème avec des données packées :
En DDS, RPG ou SQL erreur à la lecture.
En SQL, impossible de copier les données.

Vous voulez avoir des informations sur des exécutions SQL.

La meilleure méthode est de prendre la requête que vous voulez analyser et de l’exécuter dans Visual Explain.

Mais ce n’est pas toujours possible , dans le cas d’une chaine avec du SQL embarqué .

Si vous avez fait du SQL statique vous pouvez avoir des informations au niveau des programmes par PRTSQLINF.

Dans certains cas, on n’a pas ces possibilités. Le plus simple est alors de ce mettre en debug et d’exécuter votre chaine, attention ce n’est pas un mode à mettre en place en production, ça augmente considérablement les informations de log.

Vous devez démarrer le debug, le plus souvent en indiquant que vous autorisez les mises à jour sur les fichiers de production.
==>strdbg updprod(*yes)

Vous lancez ensuite votre traitement qui va générer un log avec des messages de votre optimiseur SQL, ce sont des messages id CPI43xx

Il vous suffit ensuite d’analyser votre log et regarder si certains messages vous donnent des informations intéressantes.
exemple :
CPI432F index à ajouter

Pour vous aider nous avons fait cet outil DBGSQLJOB que vous retrouverez ici et qui vous facilite la démarche
https://github.com/Plberthoin/PLB/tree/master/GTOOLS

Vous soumettez votre programme comme ceci
sbmjob cmd(DBGSQLJOB cmd(call monpgm) ) job(dbgsql)

et vous obtenez un fichier dbgsql dans la bibliothèque indiquée (par défaut *curlib) qui contiendra les messages de votre optimiseur.

Vous n’avez plus qu’a les analyser ensuite par select * from DBGSQL .

Limitation :
Dans cet outil, nous traitons en commit(*none) les requêtes SQL, si ce n’est pas le cas chez vous, adaptez le mode.

Rappel :
80 % de votre performance SQL c’est les index , suivez les recommandations de INDEX ADVISOR.

Vous installez de nouveaux PCs et vous désirez savoir les ports à ouvrir pour pouvoir accéder à vos partitions IBMi.

Voici une liste de ports à ouvrir en priorité

Pour ACS :


23 requis TELNET
449 requis arborescence du serveur IBM i
8470 à 8476 (voir RDI)

Pour RDi :


446 Requis (DRDA : connecteur base de données entre serveurs)
449 (as-srvmap) requis arborescence du serveur IBM i
3825 (Debugger) : débogage RDi
4300 (STRRSESVR) débogage via RDi (permet le rappel par l’IBM i du client RDi)
8470 (as-central) requis : Gestion des licences
8471 (as-database) requis base de données
8472 (as-dtaq) requis : serveur de file d’attente de données
8473 (as-file) requis : serveur de fichiers
8474 (as-netprt) serveur d’impression réseau
8475 (as-rmtcmd) requis : envoi de commandes
8476 (as-signon) requis : serveur ouverture de session

Merci à Jean-Marie pour son aide !

, Afficher un panel de groupe

Voici un dernier poste sur la série UIM , pour vous aider à tester vos panneaux de groupe

il existe une API QUHDSPH pour faire cet affichage

Vous trouverez une exemple, une commande DSPPNLGRP ici

https://github.com/Plberthoin/PLB/tree/master/GTOOLS

.

.

Vous pouvez maintenant créer un

Un menu UIM

Convertir un menu SDA en UIM

Générer un squelette d’aide pour vos commandes

Et tester votre panneaux d’aide

Vous pouvez allez plus loin , en apprenant le langage UIM et ses subtilités.