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 = '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 = '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.