Archive pour l’année : 2025
On a de plus en plus besoin d’avoir des machines synchronisées, pour la validation des transactions, des informations de sécurité etc…
il existe un protocole qui permet de synchroniser l’horloge de votre partition c’est le protocole SNTP
On va voir ici comment le mettre en œuvre en tant que client, en sachant que vous pourriez également utiliser votre IBMI comme un serveur
Votre serveur peut être interne ou externe il est accédé sur le port 123 , si vous cherchez un serveur :
https://syrte.obspm.fr/spip/services/ref-temps/article/diffusion-de-l-heure-par-internet-ntp-network-time-protocolv
1) Paramétrage du service TCP/IP
Vous devrez bien sur arrêter et redémarrer le service pour que la modification soit prise en compte
2) Mise en œuvre par la valeur système QTIMADJ mettre QIBM_OS400_NTP pour activer
3) Suivi des mises à jour, elles sont stockées dans un répertoire /QIBM/UserData/OS400/TCPIP/NTP
Vous avez des fichiers de log QTOTxxxx , ils sont horodatés
Vous avez également dans le journal d’audit les postes de type SV (modification de valeurs systèmes) qui peuvent être analysé
Vous y retrouverez les modifications de la valeur système QTIME
Pour en savoir plus
https://www.ibm.com/docs/fr/i/7.5?topic=protocol-scenario-synchronizing-clocks-i
N’hésitez pas c’est simple et efficace
s
Voici 5 fonctions qui peuvent vous intéresser pour manipuler des dates en SQL.
je vous rappelle que pour les utiliser, vos zones doivent être au format date et
si ce n’est pas le cas vous devrez utiliser la fonction date pour vous mettre dans le format attendu
Exemple :
values date(‘2012-01-01’)
Il est fortement conseillé si manipulez des dates de vous mettre dans un format *ISO pour éviter les problèmes de bascule des dates à 6 positions
1) Vous avez besoin de connaitre le premier jour du mois
Vous avez la fonction FIRST_DAY()
values FIRST_DAY(‘2012-12-12’) ;
renverra
2012-12-01
pour le jour en cours
FIRST_DAY(current date) = current date
2) Vous avez besoin de connaitre le dernier jour du mois
Vous avez la fonction LAST_DAY()
values LAST_DAY(‘2012-12-12’) ;
renverra
2012-12-31
Pour le jour en cours
LAST_DAY(current date) = current date
3) Vous voulez connaitre le numéro du jour dans l’année , le rang julien
Vous avez la fonction DAYOFYEAR()
values DAYOFYEAR(‘2012-12-12’)
renverra
347
4) Connaitre le jour de la semaine
Vous avez la fonction DAYOFWEEK()
elle vous renverra un numéro de 1 à 7 qui est le numéro du jour dans la semaine attention 1 c’est le dimanche
values DAYOFWEEK(‘2012-12-12’)
vous renverra 4
Si vous voulez commencer le lundi
values DAYOFWEEK(‘2012-12-12’) – 1 , attention bien sur, un dimanche vous aurez 0
5) Connaitre le nombre de jours depuis le premier janvier 01
Vous avez la fonction DAYS()
values DAYS(‘2012-12-12’)
renverra
734849
Cette fonction sert souvent pour calculer le nombre de jours entre 2 dates
values DAYS(current date) – DAYS(‘2012-12-12’)
renverra
4429
Remarque
il y a d’autres fonctions que vous pouvez utiliser, mais celles la nous semblent incontournables
La journalisation des fichiers peut vous fournir plein d’informations notamment sur les modifications de données.
Pour exploiter ces informations, vous pouvez utiliser
- le DSPJRN historique
- la fonction table SQL QSYS2.DISPLAY_JOURNAL
Mais dans les 2 cas nous trouvons face à la complexité de la gestion de la zone entry_data qui contient un buffer complet de votre enregistrement base de données.
Nous proposons depuis longtemps l’outil gratuit ANAJRN que vous pouvez télécharger ici :
https://github.com/Plberthoin/PLB/tree/master/GJOURN
Il est utilisé par plusieurs clients.
Le principe est le suivant, vous devez avoir le compilateur RPG sur votre partition, puisqu’il compile dynamiquement le programme de sortie.
Mais pour faire plus Kevin et moins Robert, j’ai mis au point une méthode à base de SQL qui nous permet d’arriver au même résultat.
La table NKSQL.STAGIAIRE a été crée et journalisée :
CREATE TABLE nksql.stagiaire
(
numero_stagiaire FOR COLUMN numstag int NOT NULL WITH DEFAULT,
nom_stagaire FOR COLUMN nomstag CHAR ( 50) NOT NULL WITH DEFAULT,
prenom_stagaire FOR COLUMN prestag CHAR ( 50) NOT NULL WITH DEFAULT,
date_entree FOR COLUMN datent DATE NOT NULL WITH DEFAULT,
PRIMARY KEY (numero_stagiaire)
) ;
Quelques actions sur la table pour alimenter le journal :
INSERT INTO NKSQL.STAGIAIRE VALUES(5, 'da Caravaggio', 'Michelangelo', date(now()));
UPDATE NKSQL.STAGIAIRE SET date_entree = '2024-12-06' WHERE numero_stagiaire = 2;
INSERT INTO NKSQL.STAGIAIRE VALUES(6, 'Magritte', 'René', date(now()- 2 months)) ;
DELETE FROM NKSQL.STAGIAIRE WHERE numero_stagiaire = 6;
L’exemple est prêt, retrouvons les données !
D’abord il faut retrouver le journal, et tant qu’à avoir SQL ouvert, j’utilise QSYS2.OBJECT_STATISTICS :
select objlib,
objname,
journal_library,
journal_name,
journal_images,
omit_journal_entry,
journal_start_timestamp
from table(qsys2.object_statistics('NKSQL', '*FILE', 'STAGIAIRE'))
where journaled = 'YES';

Ensuite il faut extraire ce journal. Dans un fichier temporaire ou non :
CREATE OR REPLACE TABLE qtemp.extrac_jrn AS (
SELECT journal_code,
journal_entry_type,
entry_timestamp,
user_name,
job_name,
job_number,
program_name,
entry_data
FROM TABLE (QSYS2.DISPLAY_JOURNAL(JOURNAL_LIBRARY =>'NKSQL',
JOURNAL_NAME =>'QSQJRN',
STARTING_RECEIVER_LIBRARY =>'*CURCHAIN',
JOURNAL_CODES =>'R',
OBJECT_LIBRARY =>'NKSQL',
OBJECT_NAME =>'STAGIAIRE',
OBJECT_OBJTYPE =>'*FILE',
OBJECT_MEMBER =>'STAGIAIRE')
) AS X
ORDER BY entry_timestamp DESC)
WITH DATA ON REPLACE DELETE ROWS;
On peut consulter cette extraction de journal, mais les données de enrty_data ne sont pas lisibles
SELECT * FROM qtemp.extrac_jrn ;

La requête qui suit va permettre de nous donner l’outil d’interprétation spécifique à cette table :
select
case
when data_type = 'DATE' then 'date(interpret(substr(entry_data, '
when data_type = 'TIMESTMP' then 'timestamp(interpret(substr(entry_data, '
else 'interpret(substr(entry_data, '
end concat
case
when ordinal_position = 1 then '1'
else
(select trim(char(sum(case when data_type = 'DATE' then storage+6 when data_type = 'TIMESTMP' then storage+16 else storage end))+1)
from qsys2.syscolumns c
where a.system_table_name = c.system_table_name
and a.system_table_schema = c.system_table_schema
and a.ordinal_position > c.ordinal_position)
end concat
', ' concat
case
when data_type = 'DATE' then '10'
when data_type = 'TIMESTMP' then '26'
else trim(char(storage))
end concat
') as ' concat
case
when data_type in( 'TIMESTMP', 'DATE') then 'CHAR'
else trim(data_type)
end concat
case
when data_type = 'BIGINT' then ''
when data_type = 'SMALLINT' then ''
when data_type = 'NUMERIC' then '(' concat trim(char(a.length)) concat ', ' concat trim(char(numeric_scale)) concat ')'
when data_type = 'DECIMAL' then '(' concat trim(char(a.length)) concat ', ' concat trim(char(numeric_scale)) concat ')'
when data_type = 'CHAR' then '(' concat trim(char(a.length)) concat ')'
when data_type = 'VARCHAR' then '(' concat trim(char(a.length)) concat ')'
when data_type = 'INTEGER' then ''
when data_type = 'TIMESTMP' then '(26)'
when data_type = 'DATE' then '(10)'
end concat
Case
when data_type in ('DATE', 'TIMESTMP') then ')) '
else ') as '
end concat
trim(system_column_name) concat
',' as interpretation
from qsys2.syscolumns a
join qsys2.sysfiles b
on a.system_table_name = b.system_table_name
and a.system_table_schema = b.system_table_schema
where b.native_type = 'PHYSICAL'
and b.file_type = 'DATA'
and b.system_table_name = 'STAGIAIRE'
and b.system_table_schema = 'NKSQL'
order by ordinal_position;

Ce resulset, à une virgule près, est intégré dans la requête de visualisation de qtemp.extrac_jrn :
select journal_entry_type,
entry_timestamp,
user_name,
job_name,
job_number,
program_name,
-- à la suite le copier coller du resultset de la requête précédente :
interpret(substr(entry_data, 1, 4) as INTEGER) as NUMSTAG,
interpret(substr(entry_data, 5, 50) as CHAR(50)) as NOMSTAG,
interpret(substr(entry_data, 55, 50) as CHAR(50)) as PRESTAG,
date(interpret(substr(entry_data, 105, 10) as CHAR(10))) DATENT
from qtemp.extrac_jrn;

On voit bien les deux créations, la mise à jour et la suppression d’enregistrement.
- La partie la plus utile de cet article est celle qui produit les interpret à partir de QSYS2.SYSCOLUMNS et QSYS2.SYSFILES. Pour l’instant elle a bien fonctionné sur toutes les tables/fichiers que j’ai rencontré mais vous pouvez certainement l’améliorer !
- Vous pouvez aussi directement inclure les interpret de la dernière requête dans celle du DISPLAY_JOURNAL.
?
Vous connaissez les fonctions usage qui vous permettent de sécuriser votre système IBMi
Exemple , accès au fichier
Elles sont organisées par famille
Voici les principales
Base operating system (5770SS1)
TCP/IP connectivity utilities (5770TC1)
Backup, Recovery, and Media Services (BRMS) (5770BR1 5770BR2)
IBM i Access Client Solutions (ACS) (5770XJ1)
IBM Navigator for i
Vous avez un site de référence qui va vous permettre de vous y retrouver c’est ici
https://www.ibm.com/docs/en/i/7.5?topic=reference-supplied-function-ids
Remarque :
De plus en plus de F U sont livrées en mode interdites, ce qui sécurise votre système, attention à QIBM_NAV_ALL_FUNCTION en V7R5 par exemple
Vous connaissez tous la notion de liste de bibliothèques qui existe sur l’ibmi
la même notion existe sous unix c’est le path, et qui impacte vos commandes exécutées par exemple à partir de QSH
Vous pouvez le changer temporairement, voici la commande par exemple qui prend votre PATH et lui ajoute l’accès au commande open source de votre machine
PATH=/QOpenSys/pkgs/bin:$PATH
export PATH PASE_PATH
Vous pouvez le changer de maniére dénitive pour une utilisateur
En créant un fichier .profile à la racine de votre path
Ca devrait donner ca /home/<votreuser>/.profile
Vous lui ajouter ces commandes
# Mise en ligne commande Open source
PATH=/QOpenSys/pkgs/bin:$PATH
export PATH PASE_PATH
Vous pouvez le changer globalement pour votre partition
Vous devez créer le fichier /etc/profile et lui ajouter ces lignes
# Mise en ligne commande Open source en plus des commandes standards
export PATH=/usr/bin:.:/QOpenSys/usr/bin
Remarque :
Vous pouvez également travailler en hardcodant , ce n’est pas conseillé pour des raisons de maintenance
exemple une copie de clé publique
/QOpenSys/pkgs/bin/ssh-copy-id ….
Vous pouvez également utiliser la variable d’environnement PATH
ADDENVVAR ENVVAR(PATH) VALUE(‘/usr/bin:.:/QOpenSys/usr/bin’) LEVEL(*JOB)
Vous choisissez le niveau *SYS ou *JOB
Bien sur si vous décidez d’utiliser le fichier etc/profile ou la variable d’environnement PATH avec portée *SYS, pensez à l’impact global sur votre système
Vous connaissez au moins de nom PRUV, qui est l’outil indispensable qui vous permet de valider vos opérations avant un changement de version .
Depuis quelques temps est apparu une nouvelle option liée au CLOUD
Si tout est ok vous aurez ce résultat
Sinon, il vous faudra prévoir une étape intermédiaire pour aller vers votre future architecture.
Plus d’informations ici
https://www.ibm.com/support/pages/ibm-pre-upgrade-verification-tool-ibm-i