The CL Corner: More on the RUNSQL CL Command

CL
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Generate a report from your database using CL.

 

Last month, in "Using the SQL Select Statement with RUNSQL," we reviewed how to query, or select, data from a SAMPLE database (also created in the referenced article). In last month's program, we simply displayed, using the CL command Send Program Message (SNDPGMMSG), the results of an SQL Select as shown below.

 

Class FIRST is effective on 2012-04-13 

Class ABC is effective on 2012-04-15   

End of file                          

 

This month, we will create a report, sorting the results of the SQL Select statement by class and effective date, as shown below.

 

5/20/2012                My Sample Report                7:40:27  Page:    1

  Class  Status  Eff. Date                                                 

  ABC       2    2012-04-15                                                

  FIRST     1    2012-04-13                                                 

                       ** End of Report **       

 

One item I do need to point out up front. To create this report, we will be using the free (as in no-charge) run-time support of the Control Language for Files (CLF) product 1BVSCLF. To create reports from CL-based applications as demonstrated by the following program, you only need to install the base product option of 1BVSCLF. There is no need to install CLF fee-based options such as the CLF precompiler.

 

Also note that if you happen to already have the base option of CLF installed on your system (perhaps due to a series of "CL Corner" articles I wrote back in 2009), there is no need to re-install CLF. There have been no PTFs, related to printer file support anyway, during the past three years.

 

Before we look at the CL program, let's review the printer file we will be using. Here's the DDS source for the printer file, named MYREPORT:

 

A          R HEADING                                           

A                                  2  2DATE(*SYS *YY) EDTCDE(Y)

A                                  2 28'My Sample Report'      

A                                  2 59TIME                    

A                                    +2'Page:'                 

A                                    +1PAGNBR EDTCDE(3)        

A                                  4  5'Class'                 

A                                  4 12'Status'                

A                                  4 20'Eff. Date'             

A          R DETAIL                    SPACEB(1)               

A            CLASS          5         5                        

A            STATUS         1  0     15                        

A            EFFDATE         L       20DATFMT(*USA)            

A          R END_OF_RPT                SPACEB(2)               

A                                    26'** End of Report **'   

 

There are three record formats defined within the MYREPORT printer file:

 

  • HEADING provides a heading for the report. The heading includes the current system date, the name of the report, the time the report was created, a page number, and column headings for the data to be listed.
  • DETAIL provides the values of the &Class, &Status, and &EffDate (effective date) for the SAMPLE record being printed.
  • END_OF_RPT provides an explicit indication of the last page of the report.

 

Assuming that the previously shown source is in member MYREPORT of source file QDDSSRC, you can create the printer file using the command CRTPRTF FILE(MYREPORT) SRCFILE(QDDSSRC).

 

Here is the program to create the previously shown report using the SAMPLE database records we wrote/inserted last month. Changes from last month's program, QRYSAMPLE, related to the second SQL Select that was demonstrated, are shown below in bold.

 

Pgm                                                   

                                                      

DclF       File(Sample) OpnID(MyResults)              

Dcl        Var(&EOF)        Type(*Lgl)                 

                                                      

Dcl        Var(&RptLine)    Type(*Char) Len(16)       

 Dcl        Var(&Class)     Type(*Char) Len(5) +      

               Stg(*Defined) DefVar(&RptLine 1)       

 Dcl        Var(&Status)    Type(*Char) Len(1) +      

               Stg(*Defined) DefVar(&RptLine 6)       

 Dcl        Var(&EffDate)   Type(*Char) Len(10) +     

               Stg(*Defined) DefVar(&RptLine 7)       

                                                      

Dcl        Var(&NoDtaToWrt) Type(*Char) Len(1)        

Dcl        Var(&OvrFlwLin)  Type(*Dec)  Len(3 0)      

Dcl        Var(&CurPrtLin)  Type(*Dec)  Len(3 0)      

CrtDupObj  Obj(Sample) FromLib(*Libl) +               

             ObjType(*File) +                          

             ToLib(QTemp) NewObj(MyResults)           

                                                      

RunSQL     SQL('Insert into QTemp/MyResults +         

               (Select * +                            

                 from  Sample +                       

                 where EffDate < ''2012-05-01'' +     

                 order by Class, EffDate)') +         

              Commit(*None)                           

                                                       

CallSubr   Subr(ReadFile)                             

                                                      

Return                                                

                                                      

Subr       Subr(ReadFile)                             

  OvrDBF     File(Sample) ToFile(QTemp/MyResults)      

  ChgVar     Var(&EOF) Value('0')                      

                                                       

  OpnFCLF    FileID(MyReport) Usage(*Output) +         

             LvlChk(*No)                               

  WrtRcdCLF  FileID(MyReport) RcdFmt(Heading) +        

             RcdBuf(&NoDtaToWrt)                       

                                                       

  DoUntil    Cond(&EOF = '1')                          

                                                       

             RcvF OpnID(MyResults)                     

             MonMsg MsgID(CPF0864) Exec( +             

               ChgVar Var(&EOF) Value('1'))            

                                                       

             If Cond(&EOF *EQ '0') Then(Do)            

                ChgVar Var(&Class) +                   

                       Value(&MyResults_Class)         

                ChgVar Var(&Status) +                    

                       Value(&MyResults_Status)          

                ChgVar Var(&EffDate) +                   

                       Value(&MyResults_EffDate)         

                WrtRcdCLF FileID(MyReport) +             

                          RcdFmt(Detail) +               

                          RcdBuf(&RptLine)               

                RtvFInfCLF FileID(MyReport) +            

                           CurPrtLine(&CurPrtLin) +      

                           PrtFOvrFlw(&OvrFlwLin)        

                                                         

                If Cond(&CurPrtLin *GE &OvrFlwLin) +     

                   Then(WrtRcdCLF FileID(MyReport) +     

                                  RcdFmt(Heading) +      

                                  RcdBuf(&NoDtaToWrt))   

                EndDo                                    

             Else Cmd( +                                 

                  WrtRcdCLF FileID(MyReport) +    

                            RcdFmt(End_Of_Rpt) +  

                            RcdBuf(&NoDtaToWrt))  

  EndDo                                           

                                                  

  CloFCLF    FileID(MyReport)                     

  Close      OpnID(MyResults)                     

  DltOvr     File(Sample)                         

EndSubr                                           

                                                  

EndPgm                                             

 

Assuming that the preceding CL source is stored in member RPTSAMPLE of source file QCLSRC, you can create the program using the command CRTBNDCL PGM(RPTSAMPLE). To run the program, use the command CALL PGM(RPTSAMPLE).

 

Now let's look at what the RPTSAMPLE program is doing.

 

The first change to the program is to declare the variables used by the various record formats of the MYREPORT printer file. The IBM-provided Declare File (DCLF) command does not support printer files, so you need to handle the printer file as if it were program-described. To program-describe the various record formats of the MYREPORT printer file, you can use the Display File Field Description (DSPFFD) command. This command will provide you with the order, type, and size of variables referenced by each format.

 

As only record format DETAIL contains references to program variables (Class, Status, and EffDate), this is the only record format you need to explicitly define. Using the output of the DSPFFD command, you define one variable (&RptLine in the provided sample program), which is declared with the length (16 bytes) of the DETAIL record format. Then, using *Defined storage, you can declare the fields found in the record format (Class, Status, and EffDate). As the CL DCL command does not support the definition of zoned decimal fields (which Status is) and date fields (which EffDate is), the program declares these variables as character fields of the proper length (1 byte and 10 bytes, respectively). Later in this article, I will point out that an alternative to the use of *Defined storage exists. I believe, though, that the use of *Defined storage provides the best documentation of subsequent processing the program will be doing (in the ReadFile subroutine).

 

Having defined the layout of the DETAIL record format, the program then declares three additional variables:

 

  • &NoDtaToWrt is declared as a one-byte character variable. This variable is later used as a command parameter to represent the record format values for formats HEADING and END_OF_RPT. These record formats do not actually contain any variables and are of zero length (as determined with the DSPFFD command previously referenced). This variable is used to simply indicate that there is no data to write associated with the formats HEADING and END_OF_RPT.

 

  • &OvrFlwLin is declared as a three-digit packed decimal variable and, strictly speaking, is not necessary in the RPTSAMPLE program. As there are only two records in the SAMPLE database that meet the criteria specified by the SQL Select statement, you do not have to worry about page overflows. In real life, though, a report can span more than one page; RPTSAMPLE demonstrates how to handle this. The variable &OvrFlwLin is used to hold the overflow line of the MYREPORT printer file.

 

  • &CurPrtLin is declared as a three-digit packed decimal value and, as with the &OvrFlwLin variable, is not strictly necessary. This variable is used to hold the current print line of the MYREPORT printer file.

 

With the printer file-related declares out of the way, the next change from last month is in the ReadFile subroutine. Prior to falling into the DoUntil processing of MYRESULTS records, RPTSAMPLE opens printer file MYREPORT using the Open File using CLF (OPNFCLF) command. When opening the file, the program specifies that the file will be used for output and that level-checking is not to be used. LVLCHK(*NO) is necessary when using program-described files.

 

Having opened the printer file, the RPTSAMPLE program then writes the MYREPORT HEADING record format using the Write Record using CLF (WRTRCDCLF) command. It is here that the "dummy" variable &NoDtaToWrt is used with the RcdBuf (Record buffer) parameter of the WRTRCDCLF command.

 

Within the DoUntil loop, after successfully reading a record from MYRESULTS with the RCVF command (that is, variable &EOF is off: '0'), the program uses three CHGVAR commands to set the &RptLine *Defined variables of &Class, &Status, and &EffDate to the values read from MYRESULTS (variables &MyResults_Class, &MyResults_Status, and &MyResults_EffDate, respectively). The program then writes the MYREPORT DETAIL record format using the WRTRCDCLF command. Here, the actual record buffer of &RptLine is specified with the RcdBuf parameter.

 

It is these CHGVAR commands that prompted the use of *Defined storage when declaring the &Class, &Status, and &EffDate variables of &RptLine. An alternative approach would be to use CL's substring capability to set the proper values of &RptLine, as shown with the following three CHGVAR commands.

 

                ChgVar Var(%sst(&RptLine 1 5)) +   

                       Value(&MyResults_Class)     

                ChgVar Var(%sst(&RptLine 6 1)) +   

                       Value(&MyResults_Status)    

                ChgVar Var(%sst(&RptLine 7 10)) +  

                       Value(&MyResults_EffDate)   

 

To my way of thinking, commands such as ChgVar Var(&Class) Value(&MyResults_Class) provide better understanding of the program flow than ChgVar Var(%sst(&RptLine 1 5)) Value(&MyResults_Class), but the choice is up to you.

 

After writing each DETAIL record of the report, RPTSAMPLE uses the Retrieve File Information using CLF (RTVFINFCLF) command to determine the current print line of the MYREPORT printer file. After the RTVFINFCLF command completes, the variable &CurPrtLin will be set to the current print line. If &CurPrtLin is greater than or equal to the page overflow value (&OvrFlwLin), then the program will advance to the next page of the report using the WRTRCDCLF command.

 

When all MYRESULTS records have been processed (that is, variable &EOF is on: '1'), RPTSAMPLE prints the line "** End of Report **" using the WRTRCDCLF command (record format END_OF_RPT) and exits the DoUntil loop.

 

Upon exiting the DoUntil loop, the program closes the printer file using the Close File using CLF (CLOFCLF) command.

 

That's it. We have now created a custom report that lists selected records of the SAMPLE database, using only a CL program. If, in the past, you resorted to using a query product (with a "close enough" report layout) or writing a program using a language such as RPG, COBOL, or C, you now have another option; just use a CL program. 

 

For those of you who do have the precompiler option of CLF installed, below is the equivalent CLF program to create the report discussed earlier in this article. Changes from last month's program are shown in bold. Note that all references to OpenID(MyResults) have been removed (which is difficult for me to show in bold). To compile the program, use the command CRTBNDCLF PGM(RPTSAMPLE).

 

Pgm                                                  

                                                      

DclFCLF    FileID(MyReport)                          

DclF       File(Sample)                              

Dcl        Var(&EOF)        Type(*Lgl)               

                                                     

Dcl        Var(&OvrFlwLin)  Type(*Dec)  Len(3 0)     

Dcl        Var(&CurPrtLin)  Type(*Dec)  Len(3 0)     

                                                     

CrtDupObj  Obj(Sample) FromLib(*Libl) +              

             ObjType(*File) +                        

             ToLib(QTemp) NewObj(MyResults)          

                                                     

RunSQL     SQL('Insert into QTemp/MyResults +        

               (Select * +                           

                 from  Sample +                       

                 where EffDate < ''2012-05-01'' +  

                 order by Class, EffDate)') +      

              Commit(*None)                        

                                                   

CallSubr   Subr(ReadFile)                          

                                                   

Return                                             

                                                   

Subr       Subr(ReadFile)                          

  OvrDBF     File(Sample) ToFile(QTemp/MyResults)  

  ChgVar     Var(&EOF) Value('0')                  

                                                   

  OpnFCLF    FileID(MyReport) Usage(*Output)       

  WrtRcdCLF  FileID(MyReport) RcdFmt(Heading)      

                                                    

  DoUntil    Cond(&EOF = '1')                      

                                                   

             RcvF                                      

             MonMsg MsgID(CPF0864) Exec( +             

               ChgVar Var(&EOF) Value('1'))            

                                                       

             If Cond(&EOF *EQ '0') Then(Do)            

                WrtRcdCLF FileID(MyReport) +           

                          RcdFmt(Detail)               

                RtvFInfCLF FileID(MyReport) +          

                           CurPrtLine(&CurPrtLin) +    

                           PrtFOvrFlw(&OvrFlwLin)      

                                                        

                If Cond(&CurPrtLin *GE &OvrFlwLin) +   

                   Then(WrtRcdCLF FileID(MyReport) +   

                                  RcdFmt(Heading))     

                EndDo                                  

             Else Cmd( +                               

                  WrtRcdCLF FileID(MyReport) +         

                            RcdFmt(End_Of_Rpt)) 

  EndDo                                         

                                                

  CloFCLF    FileID(MyReport)                   

  Close                         

  DltOvr     File(Sample)                       

EndSubr                                         

                                                

EndPgm                                           

 

More CL Questions?

Wondering how to accomplish a function in CL? Send your CL-related questions to me at This email address is being protected from spambots. You need JavaScript enabled to view it..

 

Bruce Vining

Bruce Vining is president and co-founder of Bruce Vining Services, LLC, a firm providing contract programming and consulting services to the System i community. He began his career in 1979 as an IBM Systems Engineer in St. Louis, Missouri, and then transferred to Rochester, Minnesota, in 1985, where he continues to reside. From 1992 until leaving IBM in 2007, Bruce was a member of the System Design Control Group responsible for OS/400 and i5/OS areas such as System APIs, Globalization, and Software Serviceability. He is also the designer of Control Language for Files (CLF).A frequent speaker and writer, Bruce can be reached at This email address is being protected from spambots. You need JavaScript enabled to view it.. 


MC Press books written by Bruce Vining available now on the MC Press Bookstore.

IBM System i APIs at Work IBM System i APIs at Work
Leverage the power of APIs with this definitive resource.
List Price $89.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  •  

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: