(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

(IBM i fans only) Efficient WRKSPLF with WSF - How to Search string into spooled files, Sort, and Generate PDFs on IBMi

(IBM i fans only) Detecting and Handling Non-Printable Characters in DB2 SQL Using LOCATE() and REPLACE() Functions

(IBM i fans only) How to Sniff User Access