Récupérer les définitions de colonnes d’une requête SQL avec SQLDA

– La SQLDA est composée d’une en-tête de 16 octets contenant une structure décrivant les informations d’entête de SQLDA(nombre de colonnes, nombre pivot…). et de descriptions de colonnes de 80 octets pour les informations de chaque colonne.

La méthode courante d’utilisation de SQLDA :

 Exécuter une fois l’ordre SQL DESCRIBE en plaçant le nombre pivot à  0 (SQLN = 0). Cette opération permet de récupérer le nombre exact de colonnes(zones) de la requête et d’allouer la mémoire nécessaire au pointeur qui référence la SQLDA.

 Exécuter une 2ème fois l’ordre SQL DESCRIBE pour alimenter l’ensemble des descriptions de colonnes (SQLN = nbColonnes X 80 + 16 octets d’entête).

 Ensuite on se positionne sur la description de la 1ère colonne (après l’octet 16), puis on se déplace à  l’intérieur de la SQLDA de 80 en 80 pour récupérer la descrition de chaque colonne à  l’intérieur d’une DS (SQLVAR) décrivant chaque colone.

La SQLDA peut ainsi permettre d’analiser une requête sans en connaitre la structure avant l’exécution.

L’exemple fournit :

 SQLDA.RPGLE reçoit en paramètre une requête SQL et place dans un tableau le Type (A ou N) la longueur, le nb de décimales ainsi que le nom de chaque colonne composant cette requête. Si le nom de la colonne ne peut pas être déterminé (par ex SUBST(ZON1, 1, 1)), le N° de rang de la zone à  l’intérieur de la requête est retourné (par ex: « SELECT CODE, SUBST(ZON,1 , 1) » retourne ‘00002 ‘)

 Le programme ne gère pas de fetch (pour cela il faut utiliser les 2 pointeurs SQLDATA (pour les données) et SQLIND (pour les valeurs NULL).

 Dans certains cas, les descriptions de colonnes peuvent dépasser 80 octets (les BLOBs par ex). L’exemple fournit traite les types Packed, Signed, Date, Time, Timestamp et Char.

Description SQLDA

 sqldaid => Champ de type caractère codé sur 8 octets contenant la chaîne SQLDA, qui identifie la structure SQLDA.

 sqldabc => Longueur de la DS SQLDA.

 sqln => Nombre de colones à  alimenter(o au 1er appel, puis initialiser avec sqld pour récupérer l’ensemble des descriptions de colones dans sqlvar).

 sqld => Nombre de colonnes de la requête. Cette zone est défini par l’instruction DESCRIBE.

 sql_var => Champ de 80 caractères contenant la description de chaque colone.

Code source du programme de service (V5R4)

 Programme de service

Dans cet exemple (en free rpg) j’utilise, pour gérer les erreurs SQL, un programme de service « SRVERRSQL ».

Voir article sur SRVERRSQL

      // ------------------------------------------------------------------------------------------
      
      // ------------------------------------------------------------------------------------------
      //  Lecture SQLDA pour récupérer les nom de zones et leurs définition d'une requête SQL
      //  Paramètre en entée => Requête SQL 'ex: SELECT * FROM FICHIER'
      //  Nb maximum de zones=> 999
      // ------------------------------------------------------------------------------------------
     H copyright('Serge GOMES 2005') nomain
     H
     H Option( *SrcStmt : *DebugIO)
      /COPY PROTOTYPE,SGSQLDA
      /COPY PROTOTYPE,SRVERRSQL
     PSGSQLDA          B                   EXPORT
     D                 PI              *
     D Ptr_rqt                         *   VALUE
     d Nb_occurs                      5i 0

      // Sql Descriptor Area
     D SQLDA           DS                  based(pSQLDA)
     D  SQLDAID                1      8A
     D  SQLDABC                9     12B 0
     D  SQLN                  13     14B 0
     D  SQLD                  15     16B 0
     D  SQL_VAR                      80A   DIM(1)

     D SQLVAR          DS                  based(pSQLVAR)
     D  SQLTYPE                1      2B 0
     D  SQLLEN                 3      4B 0
     D   WLong                 3      3
     D   WDec                  4      4
     D  SQLRES                 5     16A
     D  SQLDATA               17     32*
     D  SQLIND                33     48*
     D  SQLNAMELEN            49     50B 0
     D  SQLNAME               51     80A

      // SQLDA pointers
     D pSQLDA          s               *
     D pSQLVAR         s               *

      // SQLDA sizes
     DnSQLDA           s              5i 0
     DszSQLDA          s             10i 0

      // Colone descrp.
     D DSCOLONE        ds                  inz
     D  pLong                  1      4b 0
     D  aLong                  4      4
     D  pDec                   5      8b 0
     D  aDec                   8      8
      // ------------------------------------------------------------------------------------------
      // Variables de travail
     D
     DW_rqt            s          32740    varying based(Ptr_rqt)
     DW_x              s              5i 0
     DPtr_zon          s               *
     Dmod_zon          ds                  occurs(999) likeds(W_DS_ZON)
     d                                     based(Ptr_zon)
     Ddtalen           s             10i 0 inz
     DPstop            s               n   inz(*off)
      // ---------------------------ENTRY----------------------------------------------------------
      /FREE
       Nb_occurs = 0;
       // Déclaration curseur
       exec sql DECLARE FILTRE CURSOR FOR SLT;
      // Préparation instruction
       exec sql PREPARE SLT FROM :W_rqt;
       if SQLCOD <> 0;
         // Erreur SQL Appel gezstion erreur sans blocage
         callp SqlErreur(sqlca;Pstop) ;
         return *null;
       endIf;
       // Lecture initiale SQLDA pour déterminer le nb de zones  (on met SQLN à  0)
       szSQLDA = 16;
       pSQLDA = %alloc(szSQLDA);
       SQLDA = *LOVAL;
       SQLN = 0;
       // description instruction
       exec sql DESCRIBE SLT INTO :SQLDA ;
       if SQLN <= SQLD;
         nSQLDA = SQLD;
         szSQLDA = (nSQLDA * 80) + 16;
         pSQLDA = %alloc(szSQLDA);
         SQLN = nSQLDA;
         // description instruction (maintenat SQLN contient le nombre de zones de la requête)
         exec sql DESCRIBE SLT INTO :SQLDA ;
       endIf;
       if SQLD > 999;
         // Erreur Limite 999 zones atteinte
         RETURN *null;
       endIf;
       // Lecture 1ère occurence requête SQL dans SQLDA
       pSQLVAR = %addr(SQL_VAR);
       Nb_occurs = SQLD;
       dtaLen = %size(W_DS_ZON) * SQLD;
          Ptr_zon = %alloc(dtalen) ;
       FOR W_x = 1 to SQLD;
         %occur(mod_zon:W_x);
      // détermine le type de la zone en cours
         W_DS_ZON = *LOVAL;
         W_DEC    = 0;
         W_TYP = 'A';
         SELECT;
     // Packed (484-485) Signed(488-489)
         when SQLTYPE = 484 or SQLTYPE = 485 or
               SQLTYPE = 488 or SQLTYPE = 489;
           W_TYP = 'N';
           if WDec <> x'00';
             aDec = WDec;
             W_DEC = pDec;
           endIf;
           aLong = WLong;
           W_LNG = pLong;
        // Date (384-385)
         when SQLTYPE = 384 or SQLTYPE = 385;
           W_LNG = 10;
        // Time (388-389)
         when SQLTYPE = 388 or SQLTYPE = 389;
           W_LNG = 10;
        // Timestamp (392-393)
         when SQLTYPE = 392 or SQLTYPE = 393;
           W_LNG = 18;
         other;
        // char
           W_LNG = SQLLEN;
         endsl;
         // Ecriture dans le tableau
         W_NOM    = SQLNAME;
         mod_zon = W_DS_ZON;
         // Lecture occurence suivante de SQLDA
         eval pSQLVAR = pSQLVAR + 80;
       EndFor;
       return Ptr_zon;
      /END-FREE
     PSGSQLDA          E
      // ------------------------------------------------------------------------------------------ 

 Prototype

      *?------------------------------------------------------------------------------------------?*
      *   Protoype pour module SGSQLDA                                                             *
      *       Réalisation : Serge GOMES                                                            *
      *?------------------------------------------------------------------------------------------?*
      /IF DEFINED(SGSQLDA)                                                                          
      /EOF                                                                                          
      /ENDIF                                                                                        
      /DEFINE SGSQLDA                                                                               
     DSGSQLDA          PR              *                                                            
     D Ptr_rqt                         *   VALUE                                                    
     d Nb_occurs                      5i 0                                                          
                                                                                                    
     DW_DS_ZON         ds            89                                                             
     D W_TYP                   1      1                                                             
     D W_LNG                   2      5b 0                                                          
     D W_DEC                   6      9b 0                                                          
     D W_NOM                  10     89             

Code source du programme (V5R2

 programme RPG

      *‚------------------------------------------------------------------------------------------€*
      *‚ Lecture SQLDA pour récupérer les nom de zones et leurs définition d'une requête SQL      €*
      *‚ Paramètre en entée => Requête SQL 'ex: SELECT * FROM FICHIER'                            €*
      *‚ Nb maximum de zones=> 80                                                                 €*
      *‚------------------------------------------------------------------------------------------€*
     H copyright('Serge GOMES 2004')
      *‚------------------------------------------------------------------------------------------€*
      *šSql Descriptor Area                                                                       €*
      *‚------------------------------------------------------------------------------------------€*
     D SQLDA           DS                  based(pSQLDA)
     D  SQLDAID                1      8A
     D  SQLDABC                9     12B 0
     D  SQLN                  13     14B 0
     D  SQLD                  15     16B 0
     D  SQL_VAR                      80A   DIM(1)
     D SQLVAR          DS                  based(pSQLVAR)
     D  SQLTYPE                1      2B 0
     D  SQLLEN                 3      4B 0
     D   WLong                 3      3
     D   WDec                  4      4
     D  SQLRES                 5     16A
     D  SQLDATA               17     32*
     D  SQLIND                33     48*
     D  SQLNAMELEN            49     50B 0
     D  SQLNAME               51     80A
      *Å¡SQLDA pointers
     D pSQLDA          s               *
     D pSQLVAR         s               *
      *Å¡SQLDA sizes
     DnSQLDA           s              5i 0
     DszSQLDA          s             10i 0
      *Å¡Colone descrp.
     D DSCOLONE        ds                  inz
     D  pLong                  1      4b 0
     D  aLong                  4      4
     D  pDec                   5      8b 0
     D  aDec                   8      8
      *‚------------------------------------------------------------------------------------------€*
      *Å¡Variables de travail
     D
     DW_X              s              5i 0
     DP_RQT_SQL        s           9999
     DW_RQT_SQL        s                   LIKE(P_RQT_SQL)
     DW_DS_ZON         ds            89    INZ
     D W_TYP                   1      1
     D W_LNG                   2      5b 0
     D W_DEC                   6      9b 0
     D W_NOM                  10     89
     DW_TB_ZON         s                   DIM(80) LIKE(W_DS_ZON) INZ
      *‚---------------------------ENTRY----------------------------------------------------------€*
     C     *ENTRY        PLIST
     C                   PARM                    P_RQT_SQL
     C                   EVAL      W_RQT_SQL = %TRIM(P_RQT_SQL)
      *Å¡Déclaration curseur
     C/EXEC SQL
     C+  DECLARE FILTRE CURSOR FOR SLT
     C/END-EXEC
      *Å¡Préparation instruction
     C/EXEC SQL
     C+  PREPARE SLT FROM :W_RQT_SQL
     C/END-EXEC
     C                   IF        SQLCOD <> 0
      *ˆErreur SQL
     C                   EVAL      *INLR = *ON
     C                   RETURN
     C                   ENDIF
      *Å¡Lecture initiale SQLDA pour déterminer le nb de zones  (on met SQLN à  0)
     C                   EVAL      szSQLDA = 16
     C                   EVAL      pSQLDA = %alloc(szSQLDA)
     C                   EVAL      SQLDA = *LOVAL
     C                   EVAL      SQLN = 0
      *Å¡description instruction
     C/EXEC SQL
     C+  DESCRIBE SLT INTO :SQLDA
     C/END-EXEC
     C                   IF        SQLN <= SQLD
     C                   EVAL      nSQLDA = SQLD
     C                   EVAL      szSQLDA = (nSQLDA * 80) + 16
     C                   EVAL      pSQLDA = %alloc(szSQLDA)
     C                   EVAL      SQLN = nSQLDA
      *Å¡description instruction (maintenat SQLN contient le nombre de zones de la requête)
     C/EXEC SQL
     C+  DESCRIBE SLT INTO :SQLDA
     C/END-EXEC
     C                   ENDIF
     C                   IF        SQLD > 80
      *ˆErreur Limite 80 zones atteinte
     C                   EVAL      *INLR = *ON
     C                   RETURN
     C                   ENDIF
      *Å¡Lecture 1ère occurence requête SQL dans SQLDA
     C                   EVAL      pSQLVAR = %addr(SQL_VAR)
     C                   FOR       W_X = 1 to SQLD
      *Å¡détermine le type de la zone en cours
     C                   EVAL      W_DS_ZON = *LOVAL
     C                   EVAL      W_DEC    = 0
     C                   EVAL      W_TYP = 'A'
     C                   SELECT
     C                   WHEN      SQLTYPE = 484 or SQLTYPE = 485 or             Packed
     C                             SQLTYPE = 488 or SQLTYPE = 489                Signed
     C                   EVAL      W_TYP = 'N'
     C                   IF        WDec <> x'00'
     C                   EVAL      aDec = WDec
     C                   EVAL      W_DEC = pDec
     C                   ENDIF
     C                   EVAL      aLong = WLong
     C                   EVAL      W_LNG = pLong
     C                   when      SQLTYPE = 384 or SQLTYPE = 385                 Date
     C                   EVAL      W_LNG = 10
     C                   when      SQLTYPE = 388 or SQLTYPE = 389                 Time
     C                   EVAL      W_LNG = 10
     C                   when      SQLTYPE = 392 or SQLTYPE = 393               Timestamp
     C                   EVAL      W_LNG = 18
     C                   other                                                   Char
     C                   EVAL      W_LNG = SQLLEN
     C                   endsl
      *Å¡Ecriture dans le tableau
     C                   EVAL      W_NOM    = SQLNAME
     C                   EVAL      W_TB_ZON(W_X) = W_DS_ZON
      *Å¡Lecture occurence suivante de SQLDA
     C                   EVAL       pSQLVAR = pSQLVAR + 80
     C                   ENDFOR
     C                   EVAL       *INLR = *ON
     C                   return
      *‚------------------------------------------------------------------------------------------€*
Print Friendly, PDF & Email