(IBM i fans only) Retrieving Files Used in Queries of an IBM i Library

A colleague of mine left me a real treasure trove of queries, accumulated over many years of work. It’s an endless list: over 1000 queries, all well cataloged and described.


Recently, I needed to find a query related to customer orders. Knowing that the order file is called OTORD, the customer file CLCLI, and the sample file CMCMP, I started opening the most likely queries. The process was long and frustrating: Open Query > Specify file selections > View the files used > Close Query. Stressful, right?  

So, I thought I’d let our trusty IBM i do some work for me.


I created a procedure to retrieve the list of files used by all the queries in a specific library, with the ability to add some filters to answer more specific questions. For example:  

Which queries use the files OTORD00F, CLCLI00F, and CMCMP00F?  

The result? A clean and well-organized subfile:





Don't forget to set the screen to 132 columns!




The source code follows:

FQRCL       CLP

/******************************************************/         
/* RETRIEVE FILES USED IN QUERIES OF AN IBM I LIBRARY */         
/******************************************************/         
             PGM        PARM(&LIBRARY)                           
             DCL        VAR(&LIBRARY) TYPE(*CHAR) LEN(10)        
             DCL        VAR(&CPF    ) TYPE(*CHAR) LEN(07)        
             DLTF       FILE(QTEMP/FQRFILE)                      
             MONMSG     MSGID(CPF0000)                           
             DSPPGMREF  PGM(&LIBRARY/*ALL) OUTPUT(*OUTFILE) +    
                          OBJTYPE(*QRYDFN) OUTFILE(QTEMP/FQRFILE)
             MONMSG     MSGID(CPF3033) EXEC(CHGVAR VAR(&CPF) +   
                          VALUE(CPF3033))                        
             MONMSG     MSGID(CPF3064) EXEC(CHGVAR VAR(&CPF) +   
                          VALUE(CPF3064))                        
             CALL       PGM(FQRRPG) PARM((&CPF))                 
            ENDPGM                       

 

FQRRPGV     DSPF

     A                                      DSPSIZ(27 132 *DS4)
     A                                      CHGINPDFT(CS UL HI)
     A                                      INDARA
     A                                      CA03(03 'F3=EXIT')
     A                                      CA05(05 'F5=Refr')
     A                                      REF(FQRFILE)
     A*--------------------------------------------------------
     A          R SFL1                      SFL
     A*--------------------------------------------------------
     A            S1OPT          1A  B  8  2
     A            S1LIB     R             +3REFFLD(WHLIB )
     A            S1PNAM    R            + 1REFFLD(WHPNAM)
     A            S1FNAM    R            + 1REFFLD(WHFNAM)
     A            S1RFNM    R            + 1REFFLD(WHRFNM)
     A            S1FUSG    R            + 1REFFLD(WHFUSG)
     A            S1TEXT    R            + 1REFFLD(WHTEXT)
     A*--------------------------------------------------------
     A          R FMT01                     SFLCTL(SFL1)
     A*--------------------------------------------------------
     A                                      SFLPAG(0010)
     A                                      SFLSIZ(&NBRRECS)
     A                                      OVERLAY
     A       N50                            SFLDSP SFLDSPCTL
     A        50                            SFLCLR
     A        91                            SFLEND(*MORE)
     A                                      RTNCSRLOC(&PM_RCD &PM_FLD)
     A            PM_RCD        10A  H
     A            PM_FLD        10A  H
     A            SF1NUM         4S 0H
     A            NBRRECS        5S 0P
     A                                  1 26'FILES USED BY QUERY'
     A            F1LIB     R        B  3  6REFFLD(WHLIB )
     A            F1PNAM    R        B   + 1REFFLD(WHPNAM)
     A            F1FNAM    R        B   + 1REFFLD(WHFNAM)
     A            F1RFNM    R        B   + 1REFFLD(WHRFNM)
     A            F1FUSG    R        B   + 1REFFLD(WHFUSG)
     A            F1TEXT    R        B   + 1REFFLD(WHTEXT)
     A                                   + 1'<--FILTERS'
     A                                  4 25'OR'
     A            F1FNA2    R        B  4 28REFFLD(WHFNAM)
     A                                   +11' 1=I,2=E,3=I/E,4=A,5=I/A,6=E/A,7=I-
     A                                      /E/A,8=N/S,0=N/A'
     A                                  5 25'OR'
     A            F1FNA3    R        B  5 28REFFLD(WHFNAM)
     A
     A                                  6  2'OPT'
     A                                    +1'LIBRARY   '
     A                                    +1'QUERY     '
     A                                    +1'FILE       '
     A                                    +1'FORMAT    '
     A*--------------------------------------------------------
     A          R S1CMD
     A*--------------------------------------------------------
     A            S1MSG         78A    23  2
     A                                 24  2'F3=Exit'
     A                                    +3'F5=Refresh'

Note: To compile FQRRPGV and FQRRPG, you first need to generate the FQRFILE file. To quickly generate the FQRFILE file, execute:

DSPPGMREF  PGM(yourLibrary/*ALL) OUTPUT(*OUTFILE)             OBJTYPE(*QRYDFN) OUTFILE(QTEMP/FQRFILE) 

FQRRPG      SQLRPGLE

**FREE
     // ****************************************************
     // Retrieve Files Used in Queries of an IBM i Library *
     // ****************************************************
     ctl-opt option(*nodebugio:*srcstmt:*nounref) dftactgrp(*no);

     dcl-f FQRRPGV workstn indds(Dspf) sfile(SFL1 : SF1NUM);

     // constant
     dcl-c MaxSFLrec 9999 ;

     dcl-s ReLoadSFL ind inz(*on) ;

     // alfanumeric filter fields
     dcl-s F1LIB0lke varchar(35) inz('%') ;
     dcl-s F1LIB0prv varchar(35) inz(*blanks);
     dcl-s F1PNAMlke varchar(35) inz('%') ;
     dcl-s F1PNAMprv varchar(35) inz(*blanks);
     dcl-s F1FNAMlke varchar(35) inz('%') ;
     dcl-s F1FNAMprv varchar(35) inz(*blanks);
     dcl-s F1RFNMlke varchar(35) inz('%') ;
     dcl-s F1RFNMprv varchar(35) inz(*blanks);
     dcl-s F1TEXTlke varchar(55) inz('%') ;
     dcl-s F1TEXTprv varchar(55) inz(*blanks);
     dcl-s F1FNA2lke varchar(35) inz('%') ;
     dcl-s F1FNA2prv varchar(35) inz(*blanks);
     dcl-s F1FNA3lke varchar(35) inz('%') ;
     dcl-s F1FNA3prv varchar(35) inz(*blanks);

// numeric filter fields
dcl-s F1FUSGstr zoned(02) inz(*zeros);
dcl-s F1FUSGend zoned(02) inz(*hival);
dcl-s F1FUSGprv zoned(04) inz(*zeros);

     dcl-ds Dspf qualified ;
       Exit ind pos(03) inz(*off);
       Refr ind pos(05) inz(*off);
       DoForF6 ind pos(06) inz(*off);
       SflClr ind pos(50) inz(*off);
       SflEnd ind pos(91) inz(*off);
     end-ds ;

     dcl-ds RecordDs;
       WHLIB  char(10);
       WHPNAM char(10);
       WHFNAM char(11);
       WHRFNM char(10);
       WHTEXT char(50);
       WHFUSG zoned(2);
     end-ds;

     Dcl-pi FQRRPG;
       pCPF  char(7);
     End-pi;

     // Both "exec sql" is not executed but needed at compile time.
     // It must be placed just after the last "dcl" declaration.
     // LANGIDSHR defines the sort sequence as a "shared-weight sort
     // upper/lower insensitive, special characters are treated
     // "ä" as "A"
     // "Ü" as "E" and so on.
     exec sql set option Commit = *None;
     exec sql set option SRTSEQ = *LANGIDSHR;
     // preapre the data recordset
     exec sql declare C1 cursor for
     select WHLIB ,
            WHPNAM,
            WHFNAM,
            WHRFNM,
            WHTEXT,
            WHFUSG
     from FQRFILE
     where
           WHLIB  like :F1LIB0lke
     and   WHPNAM like :F1PNAMlke
     and   WHFNAM like :F1FNAMlke
     and   WHRFNM like :F1RFNMlke
     and   WHFUSG between :F1FUSGstr and :F1FUSGend
     and   WHTEXT like :F1TEXTlke
     or
           WHLIB  like :F1LIB0lke
     and   WHPNAM like :F1PNAMlke
     and   WHFNAM like :F1FNA2lke
     and   WHRFNM like :F1RFNMlke
     and   WHFUSG between :F1FUSGstr and :F1FUSGend
     and   WHTEXT like :F1TEXTlke
     or
           WHLIB  like :F1LIB0lke
     and   WHPNAM like :F1PNAMlke
     and   WHFNAM like :F1FNA3lke
     and   WHRFNM like :F1RFNMlke
     and   WHFUSG between :F1FUSGstr and :F1FUSGend
     and   WHTEXT like :F1TEXTlke

     order by WHLIB, WHPNAM

     for read only;
     //*************************************************************
     // Start working...
     //*************************************************************
     ReLoadSFL = *on;
     dow Dspf.Exit = *off;
       if ReLoadSFL;
         LoadSFL();
       endif;
       write S1CMD;
       exfmt FMT01;
       if F1FNA2 = *blanks;
          F1FNA2 = F1FNAM;
       endif;
       if F1FNA3 = *blanks;
          F1FNA3 = F1FNAM;
       endif;
       select;
       when Dspf.Exit; // F3=Exit
         leave;
       when Dspf.Refr   ; // Refresh
          F1FNAM = '           ';
          F1FNA2 = '           ';
          F1FNA3 = '           ';
       when Dspf.DoForF6; // F6=do something
       // call a program or a subroutine
       other; // Read user options
         dow 1 = 1;
           readc SFL1;
           if Not %eof;
             if S1OPT = '1';
             // call a program or a subroutine
             endif;
             if S1OPT = '2';
             // call a program or a subroutine
             endif;
             S1OPT = '>';
             update SFL1;
             S1OPT = ' ';
           else;
             leave;
           endif;
         enddo;
       endsl;
       SetFilters();
     enddo;
     exec sql close C1;
     *inlr = *on;
     //*************************************************************
     // Load subfile
     //*************************************************************
     dcl-proc LoadSFL;
       ReLoadSFL = *off;
       // Clear subfile
       Dspf.SflClr = *on;
       write FMT01;
       Dspf.SflClr = *off;
       // Load SFL
       SF1NUM = *zero;
       exec sql close C1;
       exec sql open C1;
       // Read recordset
       dow Sqlcode = 0 and SF1NUM < MaxSFLrec;
         exec sql fetch C1 into :RecordDS;
         if sqlcode = 0;
           S1LIB  = WHLIB ;
           S1PNAM = WHPNAM;
           S1FNAM = WHFNAM;
           S1RFNM = WHRFNM;
           S1FUSG = WHFUSG;
           S1TEXT = WHTEXT;
           SF1NUM += 1;
           write SFL1;
         else;
           Dspf.SflEnd = *on; // SFLEND *END
         endif;
       enddo;
       NBRRECS = SF1NUM;
       S1MSG = %Editc(NBRRECS:'X') + ' record loaded. No more record +
       to load';
       if sqlcode = 0 and NBRRECS >= MaxSFLrec; // MaxSFLrec reached
         S1MSG = %Editc(NBRRECS:'X') + ' records loaded. Use filters +
         to limit the records to be loaded.';
       endif;

       if NBRRECS = 0;
         clear SFL1;
         SF1NUM += 1;
         S1LIB  = '*';
         S1MSG = 'No records loaded';
         if pCPF = 'CPF3033';
            S1MSG = 'Object *ALL of type QRYDFN not found';
         endif;
         if pCPF = 'CPF3064';
           S1MSG = 'Library not found';
         endif;
         write SFL1;
       endif;

     end-proc;
     //*************************************************************
     // Set filters filed
     //*************************************************************
     dcl-proc SetFilters;
       if (F1LIB <>                F1LIB0prv); // char filter
           F1LIB0prv =             F1LIB ;
           F1LIB0lke = '%' + %trim(F1LIB ) + '%';
         ReLoadSFL = *on;
       endif;

       if (F1PNAM <>               F1PNAMprv); // Name
           F1PNAMprv =             F1PNAM;
           F1PNAMlke = '%' + %trim(F1PNAM) + '%';
         ReLoadSFL = *on;
       endif;

       if (F1FNAM <>               F1FNAMprv); // Name
           F1FNAMprv =             F1FNAM;
           F1FNAMlke = '%' + %trim(F1FNAM) + '%';
         ReLoadSFL = *on;
       endif;

       if (F1RFNM <>               F1RFNMprv); // Name
           F1RFNMprv =             F1RFNM;
           F1RFNMlke = '%' + %trim(F1RFNM) + '%';
         ReLoadSFL = *on;
       endif;

       if (F1FNA2 <>               F1FNA2prv); // Name
           F1FNA2prv =             F1FNA2;
           F1FNA2lke = '%' + %trim(F1FNA2) + '%';
         ReLoadSFL = *on;
       endif;

       if (F1FNA3 <>               F1FNA3prv); // Name
           F1FNA3prv =             F1FNA3;
           F1FNA3lke = '%' + %trim(F1FNA3) + '%';
         ReLoadSFL = *on;
       endif;

       if (F1FUSG <>   F1FUSGprv); // numeric filter need str & end
           F1FUSGprv = F1FUSG;
           F1FUSGstr = F1FUSG;
           F1FUSGend = F1FUSG;
           ReLoadSFL = *on;
       endif;
       if (F1FUSG = 0); // numeric filter: if 0 set from 0 to HiVal
           F1FUSGprv = F1FUSG;
           F1FUSGstr = *zeros;
           F1FUSGend = *HiVal;
       endif;

       if (F1TEXT <>               F1TEXTprv); // Name
           F1TEXTprv =             F1TEXT;
           F1TEXTlke = '%' + %trim(F1TEXT) + '%';
         ReLoadSFL = *on;
       endif;

     end-proc;

Note: To compile FQRRPGV and FQRRPG, you first need to generate the FQRFILE file. To quickly generate the FQRFILE file, execute:

DSPPGMREF  PGM(yourLibrary/*ALL) OUTPUT(*OUTFILE)             OBJTYPE(*QRYDFN) OUTFILE(QTEMP/FQRFILE)


 

Now, from the command line, type: CALL PGM(FQRCL) PARM((yourLibrary))


Remember: You can filter all columns in the SubFile using all the yellow fields, like this:




Note that it also works with the *ALL parameter to scan your entire IBM i

Try with: 
CALL PGM(FQRCL) PARM((*ALL))

There you go!


From this idea, it’s easy to modify the process to not only work with objects of type *QRYDFN but also *PGM, *SQLPKG, *SRVPGM, and *MODULE. In my next post, I’ll be developing this concept further. Stay tuned and keep an eye out for updates!

Comments

Popular posts from this blog

SQL Indicator Variables in Embedded SQL for RPG on IBM i

Using a Parameterized Cursor in SQLRPGLE

Dynamic SQL in RPG