Tutoriels

Gestion des conflits QTEMP sur IBM i en C# avec NTi

ParLaurent Rouillot

image d’illustration de l’article

Contenu détaillé de l’article:Gestion des conflits QTEMP sur IBM i en C# avec NTi

QTEMP sur IBM i fonctionne différemment selon le job qui l'utilise, QZDASOINIT pour SQL et QZRCSRVS pour les commandes CL. Cet article explique pourquoi cela peut poser problème lors de l'intégration de programmes IBM i en C#, et comment NTi permet de le résoudre proprement.

L'IBM i est le meilleur système d’information, et cela n’est plus à démontrer. Mais la volonté de continuer à prendre en charge toutes les techniques des machines qui l’ont précédé l’oblige à supporter des ressources qui, si à une certaine époque étaient très utiles, sont aujourd’hui devenues des bizarreries.

La bibliothèque QTEMP, héritée de l'AS/400 et du S/38, en est un exemple. Elle est créée systématiquement au démarrage de tout job et supprimée lorsque celui-ci s'arrête. QTEMP appartient en exclusivité au job qui l'a créée, et elle n'est accessible que par ce qui s'exécute à l'intérieur de celui-ci.

Si les choses sont claires dans une utilisation classique de l'IBM i via les sous-systèmes QINTER et QBATCH, cela se complique lorsque les ressources de l'IBM i sont sollicitées via le sous-système QUSRWRK par les jobs de type QZDASOINIT et QZRCSRVS.

Exemple de Code

La séquence C# ci-dessous fournit exactement le même résultat : la duplication d’une table dans QTEMP, avec la création de TARTICL1 par une commande CPYF puis TARTICL2 par un ordre SQL CREATE TABLE.
Ce code utilise NTi Data Provider.

Try
{
   cx.ExecuteClCommand("CPYF FROMFILE(AIRELLES/TARTICL) TOFILE(QTEMP/TARTICL1) MBROPT(*REPLACE) CRTFILE(*YES)"); 
  
   cx.Execute("CREATE TABLE QTEMP.TARTICL2 AS (SELECT * FROM AIRELLES.TARTICL) WITH DATA");
}
catch (Exception ex)
{
   Console.WriteLine(ex.ToString());
}

💡Un point d’arrêt a été placé sur le dernier bracket.

À l'exécution, lorsque le processus atteint le point d'arrêt, on constate sur l'IBM i la présence de deux jobs dans QUSRWRK via la commande WRKACTJOB SBS(QUSRWRK) : QZDASOINIT et QZRCSRVS.

Jobs actifs QZDASOINIT et QZRCSRVS dans le sous-système QUSRWRK

  • QZDASOINIT est dédié aux opérations sur la base de données via SQL.
  • QZRCSRVS prend en charge toutes les opérations typiquement AS/400 : commandes CL, programmes, etc.

Examen de la structure de ces deux jobs

Liste de bibliothèques de QZDASOINIT :

Liste des bibliothèques du job QZDASOINIT

Liste de bibliothèques de QZRCSRVS :

Liste des bibliothèques du job QZRCSRVS

Dans les deux cas, une bibliothèque QTEMP est présente. En examinant leur contenu via l'option 5 de WRKACTJOB :

La bibliothèque QTEMP de QZDASOINIT contient le fichier TARTICL2 :

Fichier TARTICL2 présent dans QTEMP de QZDASOINIT

La bibliothèque QTEMP de QZRCSRVS contient le fichier TARTICL1 :

Fichier TARTICL1 présent dans QTEMP de QZRCSRVS

Conséquences

C'est là que les choses se compliquent.

Exécution de la requête SQL :

SELECT * FROM QTEMP.TARTICL1

⚠️ Message : fichier TARTICL1 non trouvé dans la bibliothèque QTEMP.

Exécution de la commande CL :

CPYF FROMFILE(QTEMP/TARTICL2) TOFILE(QGPL/TARTICL2) MBROPT(*REPLACE) CRTFILE(*YES)**

⚠️ Message : fichier TARTICL2 non trouvé dans la bibliothèque QTEMP.

Exécution de la requête SQL :

SELECT * FROM QTEMP.TARTICL2

✅ Tout se termine normalement.

Exécution de la commande CL :

CPYF FROMFILE(QTEMP/TARTICL1) TOFILE(QGPL/TARTICL2) MBROPT(*REPLACE) CRTFILE(*YES)

✅ Aucune erreur.

Le problème vient du fait qu'une requête SQL s'adresse à la QTEMP associée à QZDASOINIT, tandis que les commandes CL, programmes et autres ressources AS/400 utilisent la QTEMP du job QZRCSRVS.

Comme l'utilisation de QTEMP est aujourd'hui passée de mode, ce sont souvent des applications anciennes qui y ont recours. Les données utiles se retrouvent presque toujours dans la QTEMP de QZRCSRVS, donc inaccessibles par une requête SQL.

En réutilisant des programmes RPG, COBOL ou CLP existants depuis .NET, des cas de ce type peuvent survenir. Une solution consiste à utiliser un programme CLP intermédiaire pour lire le fichier dans la bonne QTEMP et remonter les données en C#. Mais cela implique d'apprendre à écrire du CL pour résoudre un problème d'infrastructure.

Une solution

La fonction C# suivante reçoit en paramètres la connexion IBM i préalablement établie (cx), le nom du fichier QTEMP à exploiter (QtempFile), et retourne les données dans une DataTable.

Pour clarifier les commentaires, les notations suivantes indiquent à quelle QTEMP s'adresse chaque instruction :

  • QZDASOINIT/QTEMP
  • QZRCSRVS/QTEMP

Cette fonction déplace le fichier souhaité de QZRCSRVS/QTEMP vers QZDASOINIT/QTEMP pour le rendre accessible via une requête SQL.

public static DataTable QtempDt (NTiConnection cx, string QtempFile)
{
    var adapter = new NTiDataAdapter();
    DataTable dt = new DataTable();
    var cmd = cx.CreateCommand();

    cmd.CommandText = "SELECT * FROM QTEMP." + QtempFile;
    adapter.SelectCommand = cmd;

    // La récupération des données se fait via SQL : le fichier QtempFile
    // sera recherché dans la QTEMP associée au job QZDASOINIT.

    try
    {
      // Si le fichier a été créé par une requête SQL, cette instruction
        // s'exécute avec succès (fichier présent dans QZDASOINIT/QTEMP).
        adapter.Fill(dt); 
    }
    catch 
    {
        // Sinon, il faut copier le fichier depuis QZRCSRVS/QTEMP.
        // Une bibliothèque temporaire est créée avec un nom unique
        // basé sur l'heure courante (HHMMSS) pour éviter les doublons.

        string BibTemporaire = "LB" +
            DateTime.Now.ToString().Substring(11).Replace(":", "");

        cx.ExecuteClCommand("CRTLIB LIB(" + BibTemporaire + ")");
        
        // Utilise QZRCSRVS/QTEMP : copie du fichier dans la bib temporaire
        cx.ExecuteClCommand("CPYF FROMFILE(QTEMP/" + QtempFile + ") TOFILE(" +
            BibTemporaire + "/" + QtempFile + ") MBROPT(*REPLACE) CRTFILE(*YES)");
        
         // Création du fichier dans QZDASOINIT/QTEMP avec copie des données
        cx.Execute("CREATE TABLE QTEMP." + QtempFile + " AS (SELECT * FROM " +
            BibTemporaire + "." + QtempFile + ") WITH DATA");

        // Suppression de la bibliothèque temporaire
        cx.ExecuteClCommand("DLTLIB LIB(" + BibTemporaire + ")");

        // Les données sont intégrées à la DataTable
        adapter.Fill(dt);

        // Suppression du fichier QtempFile dans QZDASOINIT/QTEMP
        cx.Execute("DROP TABLE QTEMP." + QtempFile);
    }

    return dt;
}

Sa rapidité d'exécution permet d'insérer cette succession d'instructions sans pénaliser les temps de réponse.

Bonus

Tout au long de cet article, les jobs QZDASOINIT et QZRCSRVS ont été évoqués. Ces jobs sont des travaux à démarrage anticipé : une fois démarrés, ils restent à l'état *WAIT, disponibles pour accueillir une nouvelle requête. Ils sont sollicités par ODBC, OleDb, ACS, et bien d'autres, dont NTi Data Provider.

Cet état *WAIT est utile pour compenser la latence des outils externes : le job est prêt dans l'IBM i, il suffit de le réveiller. La capture ci-dessous montre les deux jobs après une requête depuis un programme C# utilisant OleDb :

La capture d’écran ci-dessous montre les deux jobs ayant servi à une requête à partir d’un programme en C# qui utilise OleDb :

Jobs QZDASOINIT et QZRCSRVS en état TIMW sur IBM i

Les deux jobs sont à l'état *TIMW, prêts pour une nouvelle demande. Le problème : la QTEMP de la demande précédente est toujours présente, avec les incohérences que cela peut entraîner.

Normalement, ces jobs disparaissent après un certain temps d'inactivité. Mais il arrive qu'ils restent indéfiniment à l'état *TIMW sans être réutilisés, provoquant un empilement dans QUSRWRK difficile à gérer.

Ce risque n'existe pas avec NTi Data Provider.

Les jobs QZDASOINIT et QZRCSRVS démarrés par NTi ne passent jamais à l'état *TIMW ou *PSRW lorsque la session C# est terminée. Ils sont systématiquement détruits, même lors d'un arrêt brutal du programme, et redémarrés et réinitialisés à chaque nouvelle session. Il n'y a aucun risque de réutilisation ultérieure, sans impact sur les performances.


Laurent Rouillot

Démarrez dès maintenant

Récupérez votre licence d’essai gratuite en ligne
et connectez vos applications .NET à votre IBM i en quelques minutes.

Créez votre compte

Connectez-vous au portail Aumerial, générez votre licence d’essai et activez NTi sur votre IBM i en quelques instants.

Démarrer l’essai

Ajouter NTi à votre projet

Installez NTi Data Provider depuis NuGet dans Visual Studio et référencez-le dans votre projet .NET.

Voir la documentation

Besoin d’aide ?

Si vous avez des questions sur nos outils ou sur les options de licence, notre équipe est disponible pour vous aider.

Nous contacter
30 jours d’essai gratuit activation immédiate sans engagement aucun composant à installer côté IBM i