Intercepter les erreurs SQL en RPG ile
ven 15 novembre 2019SQL c’est bien MAIS !
En RPG ile, quand une requête SQL se passe mal, le SQLCODE est négatif tout le monde le sait. Cependant dans le cas du code SQL embarqué dans du RPG ile, si le code SQL est inférieur à zéro, le RPG ne peux pas l’intercepter avec un monitor par exemple.
La solution est d’écrire une procédure qui va provoquer une erreur interprétable par les opérations d’interception (Monitor, *PSSR, Exception/error system…).
**FREE ctl-opt nomain; ctl-opt datedit(*ymd) decedit('0,'); ctl-opt option(*nodebugio:*srcstmt:*nounref) CCSIDCVT(*LIST); ctl-opt bnddir('QC2LE'); // API d'information des JOBs Dcl-pr rtvJobInf EXTPGM('QUSRJOBI'); Receiver char(100) OPTIONS(*VARSIZE); // Receiver variable RcvLen int(10:0) CONST; // Length of receiver FmtName char(8) CONST; // Format name ObjName char(20) CONST; // Object and library ObjType char(10) CONST; // Object type // Optional 1 ErrorCode char(32767) OPTIONS(*VARSIZE:*NOPASS); // Error Code end-pr; // Job information dcl-ds jobi0100 LikeDS(JOBI0100_t); //---------------------------------------------------------------- // QMHSNDPM - send program messages //---------------------------------------------------------------- dcl-pr QMHSNDPM extpgm('QMHSNDPM'); *n char(7) const; // MessageId *n char(20) const; // MessageFile *n char(512) const options(*varsize); // MessageData *n bindec(9) const; // MessageDataL *n char(10) const; // MessageType *n char(128) const options(*varsize); // CallStkEntry *n bindec(9) const; // CallStkCount *n char(4) const; // MessageKey *n char(120) options(*varsize); // ErrApi end-pr; // send program message dcl-ds sndpgmmsg; msgid char(7) inz('CPF9898'); msgfile char(20) inz('QCPFMSG QSYS '); msgdataL bindec(9) inz(512); msgtype char(10) inz('*COMP '); msgmsgq char(11) inz('* '); msgstack bindec(9) inz(1); msgkey char(4); end-ds; dcl-s msgdata char(512); //---------------------------------------------------------------- // sql communication area //---------------------------------------------------------------- dcl-ds SQLCA; SQLCAID char(8); SQLCABC bindec(9); SQLCODE bindec(9); SQLERRML bindec(4); SQLERRMC char(70); SQLERRP char(8); SQLERRD char(24); SQLER1 bindec(9) overlay(sqlerrd:1); SQLER2 bindec(9) overlay(sqlerrd:5); SQLER3 bindec(9) overlay(sqlerrd:9); SQLER4 bindec(9) overlay(sqlerrd:13); SQLER5 bindec(9) overlay(sqlerrd:17); SQLER6 char(4) overlay(sqlerrd:21); SQLWARN char(11); SQLSTATE char(5); P_SQLCA char(136) pos(1); end-ds; dcl-pr Sleep int(10) extproc('sleep'); *n uns(10) value; // Seconds end-pr; dcl-s rc int(10); //---------------------------------------------------------------- dcl-proc SQLErreur export; dcl-pi SQLErreur; pSqlca char(136) const; pStop ind options( *nopass ); end-pi; dcl-s wStop ind inz(*on); Jobi0100.Job = '*'; Jobi0100.IntJobID = *Blanks; Jobi0100.Status = 'JOBI0100'; if %Parms =2; wStop = pStop; endif; P_SQLCA = pSqlca; select; // Erreur détectée when SQLCODE < 0; msgtype = '*DIAG'; // cpf message... If SQLER1 > 0; msgid = %editw(%dec(SQLER1:7:0):'0 '); %subst(msgid:1:3) = 'CPF'; else; // cpd message... If SQLER2 > 0; msgid = %editw(%dec(SQLER2:7:0):'0 '); %subst(msgid:1:3) = 'CPD'; else; msgid = %editw(%dec(SQLCODE*-1:7:0):'0 '); %subst(msgid:1:3) = 'SQL'; %subst(msgfile:1:10) = 'QSQLMSG'; EndIf; EndIf; // Avertissement when SQLCODE > 0; msgid = %editw(%dec(SQLCODE:7:0):'0 '); %subst(msgid:1:3) = 'SQL'; %subst(msgfile:1:10) = 'QSQLMSG'; // Exécution SQL OK other; msgid = 'SQL' + SQLER6; %subst(msgfile:1:10) = 'QSQLMSG'; endsl; // message text If SQLERrml > 0; msgdata = SQLERrmc; msgdataL = SQLERrml; EndIf; // Retrouve les attributs du Job en cours rtvJobInf (jobi0100:Jobi0100.BytesRet:Jobi0100.Status: Jobi0100.Job:Jobi0100.IntJobID:Errapi); // Job inter-actif ? If Jobi0100.Type = 'I'; msgtype = '*STATUS'; msgmsgq = '*EXT'; msgstack = 0; QMHSNDPM (msgid:msgfile:msgdata:msgdataL:msgtype: msgmsgq:msgstack:msgkey:ErrApi); rc = sleep(1); msgtype = '*DIAG'; msgmsgq = '*'; Endif; // Message d'échappement If SQLCODE < 0 and wStop; QMHSNDPM (msgid:msgfile:msgdata:msgdataL: '*ESCAPE':'*CTLBDY':1:msgkey:ErrApi); EndIf; end-proc;
Voici ci-dessous les explications sur la procédure utilisée :
- Lignes 9 à 17, nous déclarons le prototype de réception de l’API QUSRJOBI.
- Ligne 20 nous déclarons la DS du format JOBI100 de l’API QUSRJOBI
- Lignes 25 à 35, nous déclarons le prototype de l’API QMHSNDPM qui nous servira en envoyé des messages programme
- Lignes 38 à 46 la DS sndpgmmsg va recueillir les informations relatives à l’erreur SQL interceptée. Elle est initialisé avec le code message CPF9898, puis selon le code SQL reçu, elle sera mise à jour avec des informations supplémentaires.
- Ligne 48 est déclarée la variable msgdata qui reçoit les valeurs qui prennent la place des parties variables du message (SQLERRMC).
- Lignes 54 à 71, SQLCA est déclaré car ce source est déclaré en type RPGILE.
- Lignes 73 à 75 prototype de la procédure sleep qui a pour fonction de faire un délai, comme la commande CL DLYJOB.
- Ligne 80 à 84 déclaration de l’interface procédure, le 1er paramètre contient le SQLCA de la requête que l’on souhaite et qui sera contrôlée pour extraire une erreur éventuelle. Le 2me paremètre indique s’il faut ou non envoyé un message d’échappement en cas de SQLcode négatif, par défaut « oui ».
- Lignes 95 à 123, contrôles des différents niveaux de SQLERR pour déterminer quels type de message (CPF, CPD, SQL) il s’agit. Les numéro de message sont extrait des variables SQLERRx.
- Lignes 125 à 129, on extrait le texte du message.
- Lignes 131 à 143, on affiche un message en cas de travail interactif.
- Lignes 145 à 148, si le SQLcode est négatif et que l’on souhaite une interception de message par le système, on envoi un message d’échappement.