Thursday, September 20, 2018

Closing all the files with one operation in RPG

closing multiple files with one rpg close operation code

There are times I stumble across things in the IBM manuals I felt I should have known. One of these is the ability to close all the files in a program with just one close statement. I have no idea how old this ability is, but it is relevant if you are programming your RPG in a modern manner.

Before main procedures were introduced to the RPG world, our programs were simple. We defined our files, and knew that they would close when the program ended.

01  **free
02  ctl-opt option(*nodebugio:*srcstmt:*nounref) ;

03  dcl-f PFILE1 ;
04  dcl-f DSPFILE workstn ;
05  dcl-f PRTFILE printer ;

06  *inlr = *on ;

Using a main procedure has allowed us the freedom of not using the RPG cycle. My programs, like the one above, still executes the RPG cycle and it is the cycle that closes the files for me.

01  **free
02  ctl-opt main(Main) option(*nodebugio:*srcstmt:*nounref)
03           dftactgrp(*no) ;

04  dcl-pr Main extpgm('PGM2') ;
05  end-pr ;

06  dcl-f PFILE1 ;
07  dcl-f DSPFILE workstn ;
08  dcl-f PRTFILE printer ;

09  dcl-proc Main ;
10    dcl-pi *n ;
11    end-pi ;

12  end-proc ;

If you are not familiar with main procedures the major differences between this and the previous program are…

Lines 2 and 3: As the program contains a main procedures I need to tell the compiler the name of it in the control options, main(Main), and to "tell" the program not to run in the default activation group, DFTACTGRP(*NO).

Lines 4 and 5: I need to define the main procedures. In this example there are no incoming parameters, therefore, I only need the DCL-PR and END-PR. I also need to give the program name in the external program keyword, EXTPGM('PGM2').

Lines 6 – 8: I have defined three files in this program. If you are using SQL for all your I/O, you might still have a display or printer file you need to use.

Line 9: Start of the main procedure, which ends on line 12.

Lines 10 and 11: The procedure interface for this procedure.

There is no *INLR = *ON or RETURN. The program is "linear", in other words when it has finished executing all of the lines of code it ends. As "linear" programs do not use the RPG cycle I find that my files remain open even though the program has ended.

To see which files are open in my job I use the Display Job command, DSPJOB, and then take option 14, Display open files, if active, on the menu.

I have removed all the other files from the display just to show the ones just used by this program.

File       Library    Device      Scope       Activation Group
PRTFILE    QTEMP                  *ACTGRPDFN  QILE
DSPFILE    QTEMP      QPADEV0001  *ACTGRPDFN  QILE
PFILE1     QTEMP      RFILE1      *ACTGRPDFN  QILE

To close the files I must either end the job, by signing off, or I can use the Reclaim Activation Group command, RCLACTGRP.

 RCLACTGRP ACTGRP(QILE)

By reclaiming the QILE activation group all the open files within it are closed.

I don't want to have to code a call to QCMDEXC at the end of all my programs to execute the RCLACTGRP command. So I have to close all the files in my program. Not a big deal when there are three files, it gets to be pain when there are more, see line 15 – 20 below.

01  **free
02  ctl-opt main(Main) option(*nodebugio:*srcstmt:*nounref)
03           dftactgrp(*no) ;

04  dcl-pr Main extpgm('PGM3') ;
05  end-pr ;

06  dcl-f PFILE1 ;
07  dcl-f PFILE2 ;
08  dcl-f PFILE3 ;
09  dcl-f PFILE4 ;
10  dcl-f DSPFILE workstn ;
11  dcl-f PRTFILE printer ;

12  dcl-proc Main ;
13    dcl-pi *n ;
14    end-pi ;

15    close PFILE1 ;
16    close PFILE2 ;
17    close PFILE3 ;
18    close PFILE4 ;
19    close DSPFILE ;
20    close PRTFILE ;
21  end-proc ;

Now you can see why I like my new discovery, the CLOSE *ALL.

12  dcl-proc Main ;
13    dcl-pi *n ;
14    end-pi ;

15    close *all ;
16  end-proc ;

All of the globally defined file, files not defined within a procedure, are closed with the CLOSE *ALL.

The close all does not have to be in the main procedure, it works just as well if I have it in a subprocedure.

12  dcl-proc Main ;
13    dcl-pi *n ;
14    end-pi ;

15    CloseFiles() ;
16  end-proc ;

17  dcl-proc CloseFiles ;
18    close *all ;
19  end-proc ;

And what of files within subprocedures?

These will close when the subprocedure ends, unless I have used the STATIC keyword when I have defined the file. By using STATIC I am saying that I want the file to remain open when the subprocedure ends.

01  **free
02  ctl-opt main(Main) option(*nodebugio:*srcstmt:*nounref)
         dftactgrp(*no) ;

03  dcl-pr Main extpgm('PGM5') ;
04  end-pr ;

05  dcl-f PFILE1 ;
06  dcl-f DSPFILE workstn ;
07  dcl-f PRTFILE printer ;

08  dcl-proc Main ;
09    dcl-pi *n ;
10    end-pi ;

11    InDeeper() ;

12  close *all ;
13  end-proc ;

14  dcl-proc InDeeper ;
15    dcl-f PFILE2 static;

16  end-proc ;

When the above program has completed PFILE2 remains open. I need to close it within the subprocedure it is defined within. Below is an example of this with what I call a "closed subprocedure", the same logic works just as well in an "open procedure".

01  **free
02  ctl-opt main(Main) option(*nodebugio:*srcstmt:*nounref)
03           dftactgrp(*no) ;

04  dcl-pr Main extpgm('PGM6') ;
05  end-pr ;

06  dcl-pr InDeeper ;
07    *n ind value ;
08  end-pr ;

09  dcl-f PFILE1 ;
10  dcl-f DSPFILE workstn usropn ;
11  dcl-f PRTFILE printer usropn ;

12  dcl-proc Main ;
13    dcl-pi *n ;
14    end-pi ;

15    InDeeper(*off) ;

16    close *all ;

17    InDeeper(*on) ;
18  end-proc ;

19  dcl-proc InDeeper ;
20    dcl-pi *n ;
21      CloseFile ind value ;
22    end-pi ;

23    dcl-f PFILE2 static ;

24    if (CloseFile) ;
25      close PFILE2 ;
26      return ;
27    endif ;
28  end-proc ;

Lines 6 – 8: This is the procedure definition of my "closed subprocedure". It accepts only one parameter, which will be an indicator, and does not return anything.

Line 15: I am calling my subprocedure passing *OFF.

Line 17: I call the subprocedure a second time passing *ON.

Lines 19 – 28: Is my subprocedure, InDeeper.

Lines 20 – 22: This is the subprocedure interface, which defines that this subprocedure will accept an indicator value, which I have chosen to name CloseFile.

Line 23: My file definition with the STATIC keyword.

Lines 24 – 27: The first time this subprocedure is called, from line 15 in the main procedure, CloseFile is off and nothing happens.

The second time this is called, from line 17 in the main procedure, CloseFile is on. The file is closed and by using RETURN no more of this subprocedure is executed. This is why this statement needs to be placed immediately after the definitions in this subprocedure, and before any other processing.

Now you can see how the close all can help reduce the size of your code, and remove the need for you to close all the globally defined files on at a time.

 

You can learn more about the RPG close all operation code from the IBM website here.

 

This article was written for IBM i 7.3, and should work for earlier releases too.

4 comments:

  1. How do I close a file that was automatically opened due to EXEX SQL INSERT statement? I have a small program that inserts a record to a file and exits with *inlr - *on. But the file is still open.

    ReplyDelete
    Replies
    1. *INLR only closes files opened with the RPG cycle. Not files that you have explicitly opened in RPG using the OPEN op code. SQL is the same it does the same as the OPEN.

      To close the file you need to either compile your SQLRPGLE with the CLOSQLCSR(*ENDMOD) or insert the SQL SET OPTION into your source, see here.

      Delete
  2. Good Info. Thank you.

    ReplyDelete
  3. Simon, great read. I use the *all on the close cmd. It’s very useful and clear. Thanks for sharing and giving another teaching moment..

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.