, Conversion en format date par fonction SQL

Nous rencontrons régulièrement dans les applications historiques, des dates stockées en base de données sous des types autres que date.
Dans du numérique, 6 dont 0, 8 dont 0, dans de l’alpha, sur 6, 8 ou 10, dans des colonnes distinctes, SS, AA, MM, JJ….

Dans la plupart des applicatifs, il existe des programmes, ou des fonctions ile, permettant de convertir ces champs en « vrai » format date, en gérant les cas limite. Si date = 0, ou si date = 99999999, 29 février…

Dans des programmes avec des accès natifs à la base de données, ces programmes / fonctions remplissent leur rôle parfaitement.

Par contre dès qu’on choisit d’accèder à la base de données par SQL, nous constatons que ces programmes sont peu à peu délaissés pour des manipulations de date directement dans les réquêtes SQL, avec des requêtes alourdit à base de case et de concat.
Pour harmoniser les règles de conversion, et allèger visuellement vos requêtes, vous pouvez créer votre propre fonction SQL, qui rendra les mêmes services que les programmes existants.

Prenons l’exemple, rencontré chez un client, d’un ERP qui stocke les dates sous un type numérique de 7 dont 0. La première position contient 0 ou 1 pour le siècle. 0 =19 et 1 = 20.

Dans cette base :

  • 950118 = 18/01/1995
  • 1230118 = 18/01/2023
  • on peut trouver les valeurs 0 et 9999999 qui ne sont pas des dates, et qu’il faudra gérer lors de la conversion
  • on peut trouver des fausses dates : 29022023, 31092022…

Nous allons créer une fonction SQL qui permettra de gérer la conversion de ces colonnes en « vraie » date.

Pour la gestion des cas limites, j’ai choisi les règles suivantes, à chacun d’adapter en fonction de ses besoins :

  • 9999999 –> 31/12/9999
  • 0 soit null si 0 passé en second paramètre, soit 01/01/Année passée en second paramètre
  • les dates inexistantes –> null

Notre jeu d’essai est composé d’une table avec 3 colonnes numérique de 7 dont 0 avec 4 enregistrements :

Pour créer nos propres fonctions SQL, on peut le faire directement en mode script via un requêteur SQL, ou utiliser une fonction d’ACS qui permet une préconfiguration en mode graphique. Je vais détailler cette seconde méthode.

Dans le bloc « Base de données » d’ACS, sélectionner l’option Schémas

Déplier l’arborescence, de votre base de données et Schémas.


Il va falloir se positionner sur le schéma (la bibliothèque) qui contiendra la fonction SQL.
Je vous conseille d’utiliser la bibliothèque contenant vos données métier, pour en faciliter l’utilisation dans vos applications.
Si les données sont en ligne, la fonction le sera aussi !

Déplier l’arboresence au niveau du schéma souhaité et cliquer sur l’item « Fonctions ».

La liste des fonctions déjà existantes dans ce schéma apparait dans la partie droite….

Par clic droit sur l’item « Fontions », choisir dans le menu, « Nouveau », puis « SQL »

Dans la fenêtre de paramétrage, on va se déplacer d’onglet en onglet.

Saisir le nom pour votre fonction.

  • Onglet « Paramètres » par le bouton sur la droite « Ajout… », on va déclarer les paramètres en entrée de la fonction, en premier un numérique de 7 dont 0 et en second un numérique de 4 dont 0 pour passer une année par défaut en cas de 0.

Pour l’année par défaut, nous ajoutons une valeur par défaut, 0. Nous verrons l’intérêt de cette valeur par la suite.

  • Onglet « Retours », nous déclarons la valeur de retour, soit une date au format date.
  • Onglet Options : cet onglet permet de fixer le contexte d’éxecution de la fonction, et donc le bon fonctionnement de la fonction ainsi que son optimisation.
    Par rapport aux valeurs par défaut, j’ai modifié 2 paramètres :

    – Accès aux données. Ma fonction n’accèdera à aucune table, j’ai donc choisi l’option « Contient SQL ». Si ma fonction devait accèder à des tables en lecture uniquement, il faudrait laisser l’option par défaut « Lit des données SQL », enfin si la fonction devait mettre à jour des tables, l’option « Modifie des données SQL ».

    – Même valeur renvoyée à partir d’appels successifs pour des paramètres identiques. En cochant cette case, j’autorise le moteur SQL à enregistrer le résultat de la fonction avec les paramètres d’appel dans le cache SQL et de réutiliser ce résultat sans éxécuter la fonction en cas d’appel avec les mêmes paramètres.
    1230118 renverra toujours 18/01/2023. Et Date = 0, an par défaut = 0 renverra toujours null…

    Ces paramètres sont à fixer selon l’usage mais aussi le code utilisé dans la fonction.
  • Onglet « Corps de routine », il ne reste plus qu’à coder la fonction en SQL procédural.
    Pour rappel, on encadre le code par « BEGIN (sans ;) / end (sans ; ) », dans l’interface graphique… Dans un script SQL, il faut bien ajouter un « ; » après le end.
    Les conditoinnements ne prennent de « ; » que sur le end
    Les instructions autres se terminent par un « ; »
    On peut utiliser des variables de travail, il faut les déclarer par …. Declare !
    La valeur retour est renvoyées par l’instruction return.

Vous pouvez maintenant utiliser votre fonction, que ce soit par un scripteur SQL, dans vos SQLRPGLE, dans des scripts SQL lancé par runsqlstm…

Vous constaterez que je n’ai passé que le 1er paramètre à ma fonction. Le second ayant une valeur par défaut, il devient facultatif. Ce qui veut dire, que si vous avez besoin de rajouter un paramètre à une fonction SQL déjà existante, ajouter une valeur par défaut permet de ne pas avoir à reprendre l’existant. Seuls les cas nécéssitant ce nouveau paramètre seront à traiter.

Si nous lançons la fonction sur la colonne DATEERP3 qui contient une valeur qui n’est pas une date, 1230229, la requête plante :

Les résultats s’arrêtent dès le crash et ne renvoit que les deux premiers enregistrements dont la résolution de la fonction était ok :

Il faut ajouter une gestion d’erreurs à notre fonction.

Et c’est une règle d’or sur les fonctions personnalisées. Vous n’avez le droit à aucun plantage de la fonction, au risque de traiter dans vos programmes des résultats tronqués si la gestion des sqlcode / sqlstate n’est pas faite.

En début de script, je rajoute le monitoring, sur le SQLSTATE renvoyé par l’erreur et je choisit de renvoyer la valeur null :

N’ayant pas beaucoup d’instructions dans ma fonction, je me contente de cette gestion d’erreur. Dans des cas plus complexe, ne pas hésiter à monitorer avec un SQLEXCEPTION

Maintenant la fonction renvoie null si la date n’existe pas et nous avons les résultats pour nos 4 enregistrements.

Si nous lançons la fonction sur la colonne DATEERP2 qui contient des dates valides et la valeur 9999999, nous constatons

que deux dates ne sont pas traduites :

Vu que je n’ai monitoré que le sqlstate 220007, nous savons que c’est pour date invalide que la conversion n’a pas eu lieu.
Le problème vient du format de date dans ma fonction SQL, par défaut *YMD

Ce format de date est limité dans le temps au 31/12/2039…

Il faut passer en format *iso pour convertir des dates au-delà de 2039, et donc pour cela modifier le set option par défaut.

Maintenant, tout fonctionne comme voulu, ma fonction est opérationnelle :

Vous pouvez continuer à utiliser les programmes existants pour ces conversion, mais dans ce cas il faut interdire à vos développeurs la conversion dans les requêtes SQL.

L’avantage de passer par une fonction SQL, c’est que cette fonction peut aussi être utilisée par des applicatifs distants qui viennent requêter sur la base de données. Appli web, bien entendu, mais aussi les ETL, comme Talend, et de garder la main sur les règles de conversion, plutôt que de les déporter sur chaque outil.

, , Récupérer les logs d’une commande shell QSH

Interrogation SQL des tables système


Le passage de commandes shell est tout à fait possible dans les programmes de CL (langage de contrôle sur IBM i) via QSH CMD(&maCommande). Si dans une session QSH (STRQSH) l’état des logs s’affiche, nous n’avons pas ces retours quand les commandes shell sont passées via un programme CL.

plus d’informations sur le shell QSH sous IBM i : LE SHELL INTERPRETER

Contexte

Les deux écrans suivants permettent de tester l’authentification à Github (plateforme de versionnage du code, de contrôle et de collaboration ) ; ce travail servira d’exemple à notre article :

  • Commandes manuelles, dans une session QSH avec les logs (en rouge)
  • Commande automatisée dans un programme CL, sans retour de logs

Il existe tout de même un moyen pour récupérer les logs d’une commande shell passée via un programme CL ; l’interrogation des tables système par requête SQL.

Principe

Par un programme CL, les commandes QSH sont soumises dans un travail via SBMJOB

Plusieurs paramètres sont nécessaires :

  • la commande QSH (ici &QSH)

  • le travail doit être identifiable par un nom &JOBNAME, une bibliotheque &LIB, une file de travail &JOBQ, une file de sortie &OUTQ et un utilisateur &USER ; que l’on peut déclarer de cette manière

les noms de variables et leurs valeurs sont arbitraires

  • l’instant t de l’exécution &TIMESTAMP

Ce sont ces paramètres qui alimentent notre requête SQL, et nous permettent de trouver les logs.

VUE qsys2.output_queue_entries

La première composante de notre requête SQL est la récupération des infos du travail en fonction des paramètres, expliqués ci-dessus, via la vue système qsys2.output_queue_entries.

Encapsulation et récupération de la liste des logs d’un travail

Nous récupérons la dernière ligne entrée dans qsys2.output_queue_entries (ORDER BY create_timestamp DESC FETCH FIRST ROW ONLY) que nous encapsulons dans un WITH ; ce qui correspond aux identifiants associés au dernier travail lancé par le programme CL.

Le résultat du WITH (alias lastLog ici) est passé dans un SELECT sur la vue systools.spooled_file_data pour récupérer la liste des spools associés à la commande QSH (variable &QSH dans notre exemple): WHERE spooled_data like trim(:log) || '%'.

Nous obtenons les spools associées à une (ou plusieurs) commandes QSH lancé(es) via un programme CL

Intégration dans un programme

A ce niveau, nous sommes capable d’obtenir les logs générées par une commande QSH pour consultation ; en passant nos requêtes SQL dans un exécuteur de script. Voyons maintenant comment il est possible d’obtenir ces logs par un programme et d’adapter le traitement en fonction de leurs valeurs.

Programme CL

L’idée est d’avoir notre programme CL (ici ggitAuth.clle) qui :

  • soumet le travail,

  • appelle un programme SQLRPGLE qui retourne la log (ici getqshlog.sqlrpgle),

  • effectue le traitement en fonction de la valeur de la log retournée par le programme SQLRPGLE.

Programme SQLRPGLE

Le programme SQLRPGLE reçoit en paramètre :

  • la valeur de la log ciblé log,
  • la file de sortie OUTQ,
  • la file de travail JOBQ,
  • le nom du travail JOBNAME,
  • le temps d’exécution TIMESTAMP.

Par un exec sql, nous retrouvons l’interrogation de table vue précédemment. Celle-ci est intégrée dans une boucle pour gérer le délai d’écriture dans la table au moment du passage de la commande QSH :

Enfin nous mettons dans une variable la valeur de la log ; qui prend ‘Log not found’ en cas d’échec de la requête (sqlCode <> 0).


En résumé

Il est possible dans un environnement IBM i d’exécuter des commandes shell comparables à ce qui peut se faire sur UNIX. Les logs générées par ces commandes sont consultables par interrogation de tables SQL. Pour aller plus loin, la récupération des logs pour analyse dans un programme permet la prise de décision dans ce dernier.

, , Sécuriser le SQL embarqué

SQL prend une place de plus en plus importante dans les développements.

Il faut donc prendre des mesures de protection spécifiques sur le SQL embarqué qu’on ne prenait pas forcément en RPGLE.

Par exemple si vous utilisez du cryptage avec les fonctions ENCRYPT et DECRYPT il est important de ne pas afficher la clé de cryptage.

1) Le debug

Le premier risque c’est le debug : vous allez voir la clé de décryptage, même si vous utilisez une variable host c’est facile à voir.

La solution, c’est quand vous compilez votre programme le paramètre DBGENCKEY(‘Votre_clé’)

Vous devrez désormais indiquer votre clé pour débuguer.

Attention bien à ne pas perdre cette clé qui vous sera demandée à chaque de bug !

2) Informations sur les instructions SQL embarquées

Vous avez des vues qui vous permettent de voir le code sql embarqué dans vos programmes RPGLE

exemple :

la vue QSYS2.SYSPROGRAMSTMTSTAT

Pour vous prémunir vous devrez utiliser une variable host et ainsi vous n’aurez pas la valeur de décryptage dans l’instruction …

3) Le cache sql

Vous avez l’instruction exécutée que vous pouvez consulter dans le cache SQL, par exemple par ACS

Comment faire pour que la valeur ne soit pas affichée et pas affichable dans la requête avec les variables ?

Vous avez une procédure SYSPROC.SET_COLUMN_ATTRIBUTE

CALL SYSPROC.SET_COLUMN_ATTRIBUTE(‘GENVOI’, ‘GPARAM’, ‘PWORD’, ‘SECURE YES’);

plus de detail ici

Vous avez :h dans le cache et quand vous faites <gestion instruction SQL et variables>
la zone apparait en sécure

Vous devrez faire cette opérations sur toutes les zones !

https://www.ibm.com/docs/en/i/7.4?topic=services-set-column-attribute-procedure

4) Les SQL packages

Le SQL package est un objet qui stocke des informations pour en tirer partie au cours d’une future utilisation, depuis l’arrivée du moteur SQE, ils sont utilisés en second par rapport au cache SQL

Il faut différencier 2 types de SQL PACKAGE :

  • Les sql packages qui font partie de votre programme pour les SQL statiques, vous pouvez les consulter par la commande PRTSQLINF de votre programme sqlrpgle qui produira un spool comme celui ci :

On voit que également en ayant utilisé une variable hôte que l’information n’apparait pas en claire

  • Il existe une deuxième catégorie de packages SQL qui sont dus à l’utilisation du Extended Dynamic SQL, ce sont des objets qui sont créés essentiellement pour des accès ODBC et dynamiques, ce sont des objets de type *SQLPKG, ils sont en principe créés dans la bibliothèque qui contient la base de données. SQL utilisera les informations qui sont stockées à l’intérieur quand il en aura besoin. Il est difficile de voir le contenu des ces objets , cependant vous pouvez faire un dump de cet objet, par la commande DMPOBJ comme ci-dessous , mais encore une fois pas de contenu des variables hôtes.

Ces objets peuvent être supprimés, le système les recréera automatiquement quand, par exemple, vous changez de version d’IBM i ou quand il occupe trop de place. Attention cependant, il en existe certains qui font partie de SQL comme

QSQLPKG2 de QSYS
QSQXDPKG de QSYS

En règle générale, il ne faut pas toucher à ceux dont le nom commence par Q, ni aux objets qui sont dans QSYS.

Ci dessous un lien pour en savoir plus ces objets

https://www.ibm.com/support/pages/sql-package-questions-and-answers

Complément sur la procédure SET_COLUMN_ATTRIBUTE

Merci Christian

la procédure SET_COLUMN_ATTRIBUTE cache aussi :

  • la valeur des marqueurs qui sont dans les lignes QQRID=3010 des moniteurs de performance et images instantanées de cache de plan.
  • la valeur des marqueurs qui sont dans les attributs d’un graphe Visual Explain (voir image).
  • la valeur des marqueurs dans le pseudo SQL des nœuds de VE (voir image).

Conclusion :

SQL change la donne sur certains points, vous devrez adapter votre sécurité en fonction.

Dans tous les cas, préférez une variable host à un hardcoding qui apparait en clair.

Il y a certainement d’autres choses à mettre en œuvre, mais ces quelques astuces vous donnent une idée de ce que vous devrez mettre en œuvre

, , , Utilisation d’invite sélective

Vous connaissez et utilisez l’API QCMDEXC qui permet d’exécuter un commande IBMI

Exemple

CALL QCMDEXC (‘ADDLIBLE STITE’ 25)

Vous pouvez utilisez aussi par SQL sous forme de fonction ou procédure

Et enfin elle sera utilisée si vous faites un menu par SDA ou par UIM

Pour avoir une invite sélective vous devrez indiquer un ? avant la commande

Exemple


?WRKSPLF

Pour exécuter la commande WRKSPLF en ayant un invite équivalent à si vous aviez utilisé <F4>

Mais vous pouvez vouloir que certain paramètres soient affichables sans saisie d’autre pas affichable etc …

On va pouvoir indiquer devant les paramètres de la commande 2 caractères, pour indiquer une option de traitement de l’invite

?? le paramètre est modifiable et visible
?* le paramètre est vue mais pas modifiable
?– le paramètre n’est pas affichée

Ci dessous un tableau récapitulatif

Exemple

Sur la commande de gestion des fonctions usage , dans une procédure SQL

Ce qui produira un résultat suivant ou les 2 paramètres DEFAULT et ALLOBJAUT ne seront pas affichés

Le lien à connaitre pour les informations complètes

https://www.ibm.com/docs/en/i/7.3?topic=time-using-selective-prompting-cl-commands

, , Liste des fonctions usage

Vous avez installé un nouveau système et il vous manque des fonctions usage dans navigator for i , attention il y a des fonctions qui ne sont pas administrables par cette interface mais uniquement en 5250 par la commande =>WRKFCNUSG, cela dépendra de la catégorie :

Pour gérer plus simplement les utilisateurs pour les fonctions non administrées dans navigator for i , (par exemple QIBM_DB_ZDA qui sert pour autoriser les accès ODBC) nous proposons un produit téléchargeable à cette adresse https://github.com/Plberthoin/PLB/blob/master/WRKUSRUSG/

Vous pouvez gérer le comportement globale de la fonction <F10> et les users en exception <F6>

Maintenant ,voici comment ajouter celles qui sont administrables et que vous ne voyez pas

Leur affichage dans Navigator for i peut dépendre des produits installés sur votre partition

Ouvrir l’onglet  

Ajouter des fonctions dans fonctions usage

Puis choisir dans les actions

sélectionnez l’enregistrement des fonctions

Quand vous revenez dans liste des fonctions, vous en avez maintenant beaucoup plus

On peut analyser les violations des fonctions usage, c’est les postes de type GR dans le journal d’audit.

Soit par un DSPJRN

Soit par une requête SQL

Liste des violations de Fonction Usage sur la journée précédente !

SELECT *

  FROM TABLE (

      SYSTOOLS.AUDIT_JOURNAL_GR (STARTING_TIMESTAMP => CURRENT DATE – 1 DAYS))

    WHERE FUNCTION_REGISTRATION_OPERATION = ‘USAGE FAILURE’

Conclusion :

L’utilisation de certaines de ces fonctions devient primordiale, et il faudra s’habituer à les utiliser .

Vous pouvez ajouter ou supprimer des fonctions liées à des applications installées sur votre partition dans navigator for i mais toutes ne sont pas gérées dans l’interface !

, , , Informations sur les commandes

Avec la version TR1 de 7.5 et la TR7 de la 7.4

IBM met à dispo un service (COMMAND_INFO) qui va permettre d’avoir des informations sur les commandes de votre partition IBM i.

Voici 2 idées d’utilisation de cette vue QSYS2.COMMAND_INFO

Par exemple, pour connaitre les commandes qui sont permises en environnement restreint

SELECT *
FROM QSYS2.COMMAND_INFO
WHERE ALLOW_LIMITED_USER = ‘*YES’

Par exemple, pour savoir si un programme est utilisé dans une commande lorsqu’on fait une analyse d’impact avant une modification

SELECT COMMAND_NAME,
COMMAND_PROCESSING_PROGRAM_LIBRARY,
COMMAND_PROCESSING_PROGRAM
FROM QSYS2.COMMAND_INFO
WHERE COMMAND_LIBRARY = ‘GFTP’

Conclusion :

Cette vue fournit des informations intéressantes et faciles à croiser avec d’autres éléments sur toutes les commandes présentes sur vos systèmes.

, Suivi des travaux par JOBQ

Avec la version TR1 de 7.5 TR1 et la TR7 de la 7.4

IBM met à dispo un service qui va permettre suivre les travaux soumis par JOBQ, cette information existait mais était plus compliquée d’utilisation et vous pourrez choisir les JOBQ que vous voulez suivre.

Voici la liste des services disponibles :

ADD_TRACKED_JOB_QUEUE, qui permet d’ajouter une JOBQ à superviser
CLEAR_TRACKED_JOB_QUEUE, qui permet de reseter les statistiques des JOBQ supervisées
REMOVE_TRACKED_JOB_QUEUE, qui permet de retirer une JOBQ à superviser
TRACKED_JOB_INFO, qui permet d’avoir des informations sur les travaux soumis dans cette JOBQ
TRACKED_JOB_QUEUES, qui permet de voir les JOBQ supervisées

Ci-dessous le lien de référence pour retrouver les informations essentielles

https://www.ibm.com/docs/en/i/7.4?topic=services-work-management

Principe

Vous ajoutez une JOBQ à superviser


CALL QSYS2.ADD_TRACKED_JOB_QUEUE(IASP_NAME => ‘*SYSBAS’,
JOB_QUEUE_LIBRARY => ‘SSHLIB’,
JOB_QUEUE => ‘SSHJOBQ’);

Vous pouvez voir les travaux qui sont placés par votre JOBQ

SELECT * FROM TABLE(QSYS2.TRACKED_JOB_INFO(JOB_QUEUE_LIBRARY_FILTER => ‘SSHLIB’,
JOB_QUEUE_FILTER => ‘SSHJOBQ’))
WHERE JOB_END_TIME IS NULL;

Vous pouvez avoir des informations sur les JOBQ supervisées

SELECT *
FROM QSYS2.TRACKED_JOB_QUEUES
ORDER BY IASP_NAME, JOB_QUEUE_LIBRARY, JOB_QUEUE_NAME;

Conclusion :

Ce service peut vous permettre d’avoir un suivi précis par JOBQ des jobs soumis, à l’inverse vous pourrez savoir si une JOBQ n’est pas utilisée

, , Droits et génération SQL script

Vous devez moderniser votre base de données, pour cela vous pouvez commencer par extraire le source de votre PF,
par exemple en passant par Générations d’instructions SQL dans ACS , ou en utilisant la procédure SQL de QSYS2 GENERATE_SQL Ou GENERATE_SQL_OBJECT ,


La plus part du temps on obtient un scripte SQL qui vous permettra de générer votre nouvelle table , ici un exemple ou on a enlevé les commentaires.

Que ce passe t’il au niveau des droits ?

Avant par DSPOBJAUT

par DROITS dans ACS

La liste d’autorisations

Premier effet vous pouvez avoir des différences sur les droits publics

exemple ici

Après

Vous vous retrouvez avec un droit USER DEF au lieu de *CHANGE et vous avez perdu le droit exécute, on est d’accord ca ne change rien sur une table, c’est juste un peu moins lisible quand on analyse au niveau du système

le plus gênant c’est la liste d’autorisation que vous perdez
et la cela peux changer complètement puisque vous perdez 1 voir 2 niveaux de recherches

Dans notre cas FORM06 se retrouve avec des droits *PUBLIC

Conclusion :


Après avoir modernisé vos tables, vous devez réappliquer vos droits le plus simple est de généré un objet de référence

une autre solution est de vous affranchir des listes d’autorisation qui ne sont pas générées dans SQL

, , , Tuez les jobs SSH dans QINTER

Si vous commencez à faire du SSH (en tant que client), par exemple pour faire du GitHub, vous avez des jobs qui vont rester et que vous devrez tuer par un kill ou un ENDJOB.

C’est des jobs BCI qui restent après avoir arrêté vos JOBs interactifs.

Voici un petit script CLP qui permet de faire ça, il vous suffit de le planifier tous les soirs à 23 heures par exemples.

Ici on a utiliser la fonction table QSYS2.ACTIVE_JOB_INFO et la fonction QSYS2.QCMDEXC et on packagé le tout dans un CLP …

PGM
/* Suppression des jobs SSH dans QINTER */
dcl &NBRCURRCD *dec 10
/*  Exécution de la requete SQL */
             RUNSQL     SQL('Create table qtemp/sortie as(SELECT +
                          QSYS2.QCMDEXC(''ENDJOB '' CONCAT +
                          JOB_NAME)  as resultat FROM +
                          TABLE(QSYS2.ACTIVE_JOB_INFO(SUBSYSTEM_LIST_+
                          FILTER => ''QINTER'')) X  where job_type +
                          = ''BCI'') with data') COMMIT(*NONE)
             MONMSG     MSGID(SQL0000) exec(do)
             SNDUSRMSG  MSG('Erreur dans l''exécution de la requête +
                          SQL d''épuration des jobs SSH') +
                          MSGTYPE(*INFO)
             Return
             enddo
  /* Envoi d'un message de fin */
             RTVMBRD    FILE(QTEMP/SORTIE) NBRCURRCD(&NBRCURRCD)
             if cond(&NBRCURRCD > 0) then(do)
             SNDUSRMSG  MSG(%CHAR(&NBRCURRCD) *TCAT ', job(s) SSH +
                          arrêté(s)') MSGTYPE(*INFO)
             enddo
             else do
             SNDUSRMSG  MSG('Pas de Job SSH à Supprimer ') +
                          MSGTYPE(*INFO)
             enddo
ENDPGM

Sur les job de ssh-agent vous pouvez demander le kill directement comme ceci, merci à Julien …

La commande QSH lancer par votre CL qui vous permet de soumettre se présente donc comme ça :

eval "$(ssh-agent -s)" ; // Démarrage de l'agent 
SSH ssh-add /home/jl/.ssh/github ; // Ajout de la clé à l'agent 
ssh -T git@github.com ; // Test de connexion à GitHub 
ssh-agent -k // Arrêter l'agent
, , , Date source de vos programmes

On est amené quand on fait des analyses à regarder les dates de source, on constate que ces dates sont à null pour tous les objets de type ILE.

Vous avez une vue QSYS2.PROGRAM_INFO qui permet d’avoir ces informations sur les programmes, un peu comme la commande DSPPGM.

Voici pourquoi : quand vous travaillez en OPM vous compilez des sources qui deviennent des programmes; quand vous travaillez en ILE, vous compilez des sources qui deviennent des modules, puis vous les assemblez pour créer des programmes et du coup une date de source sur un programme ILE ne veut rien dire.

En réalité un programme a un module qui s’appelle point d’entrée programme qui, quand on travaille en BND (CRTBND*), est le seul module placé dans qtemp qui est assemblé pour créer votre programme.

On voit donc que si on veut, on peut assimiler la date du source du programme à la date du module PEP, qui dans plus de 99 % des cas a le même nom que le programme.
On a une deuxième vue permet d’avoir les modules par programme, QSYS2.BOUND_MODULE_INFO.

Il faudra donc combiner les 2 vues.

par exemple :

  • Pour les programmes ILE

SELECT a.PROGRAM_NAME, a.PROGRAM_TYPE, b.SOURCE_FILE_LIBRARY, b.SOURCE_FILE,
b.SOURCE_FILE_MEMBER, b.SOURCE_CHANGE_TIMESTAMP
FROM QSYS2.PROGRAM_INFO A join QSYS2.BOUND_MODULE_INFO B
on a.PROGRAM_NAME = b.PROGRAM_NAME
and A.PROGRAM_NAME = b.BOUND_MODULE and A.PROGRAM_LIBRARY = b.PROGRAM_LIBRARY
WHERE a.PROGRAM_LIBRARY = ‘FADY’ and a.PROGRAM_TYPE = ‘ILE’

  • Pour les programmes OPM

SELECT a.PROGRAM_NAME, a.PROGRAM_TYPE, A.SOURCE_FILE_LIBRARY, A.SOURCE_FILE, A.SOURCE_FILE_MEMBER,
A.SOURCE_FILE_CHANGE_TIMESTAMP
FROM QSYS2.PROGRAM_INFO A
WHERE a.PROGRAM_LIBRARY = ‘FADY’ and a.PROGRAM_TYPE = ‘OPM’

en faisant l’union des deux requêtes vous aurez les dates de tous vos programmes ILE et OPM.

Il y a sans doute d’autres solutions mais celle-ci est très simple à utiliser.