Thursday, December 12, 2013

Easiest way to read all the members in a multiple member file

The idea for this post came from a discussion I saw on Facebook. Someone asked:

  1. In your RPG how can you read from a particular member of a file?
  2. By default which member is read?
  3. How can you read all the members of your PF in your RPG program?
  4. How can you find out which member is being read?

The answer for the first question is to use the EXTMBR in the File specifications. I will not go into details about this as it is covered in Useful keywords for your F-specs.

The answer for the second question is that when a program opens a file with more than one member, by default, it opens the first member, which is the oldest member as that was the one created first.

The third and fourth questions I combined into my own scenario: I want to have a program that reads all the members in a multi member file to find a particular record and identify which member this record is in.

I created a file, TESTPF, with two members, ONE and TWO (I know I will not win any awards for giving things in my posts interesting names).

 A          R TESTPFR
 A            FLD1          10A
 A          K FLD1

I added data to the file to allow me to track which member was being read:

Value of FLD1
Member
ONE
Member
TWO
1 1
1 2
1 3
1 4
1 5
2 1
2 2
2 3
2 4
2 5
0 0

You could use the OVRDBF command in CL to override the member parameter to all members, MBR(*ALL), see below. I have set the override scope to *CALLLVL, therefore, only programs called by this program will have the override, and when this program ends the override no longer exists.

   OVRDBF  FILE(TESTPF) MBR(*ALL) OVRSCOPE(*CALLLVL)

Or you could use the EXTMBR keyword in the File specification of the RPGLE/RPG IV program, see below.

  FTESTPF    IF   E           K DISK    extmbr('*ALL')

In both cases the members are processed in order. In other words when all of the records from the first member have been read the program will start reading then the records in the second member.

This answers the third question posed at the start of this post.

Before I show program RDALLMBR1 I want to explain the File Information Data Structure. It is a data structure that contains feedback information associated with a file. I like to use it as an externally described data structure. i have this data structures in Externally described Data Structures. Below is the DDS for my File Information Data Structure.

 A          R DUMMY
 A            FILLER1        8A
 A            FILEOPEN       1A         TEXT('File open')
 A            ENDOFFILE      1A         TEXT('End of file')
 A            FILESTATUS     5A         TEXT('File status code')
 A            OPCODE         6A         TEXT('Last operation code')
 A            ROUTINE        8A         TEXT('Routine name')
 A            SRCSTMTNBR     8A         TEXT('Source statement No.')
 A            FILLER2        8A
 A            ERRMSGID       7A         TEXT('Error message id')
 A            FILLER3       30A
 A            FILE          10A         TEXT('File name')
 A            LIBRARY       10A         TEXT('Library name')
 A            FILLER4       26A
 A            MEMBER        10A         TEXT('Member name')
 A            FILLER5      122A
 A            RCDFORMAT     10A         TEXT('Record format')
 A            FILLER6      258A

The data structure is 528 characters long, therefore, the parts I am not concerned about here by I have coded as Filler fields, FILLER1 - FILLER6.

DS field Description
FILEOPEN 1=File open.
ENDOFFILE 1=End of file encountered.
FILESTATUS File status code, I am not going to lilst them here but I am going to refer you to the IBM web site here.
OPCODE Last operation code performed to file.
ROUTINE Routine last operation occured in.
SRCSTMNBR Useful if you compile you RPGLE with OPTION(*SRCSTMT) as it will give the source statement line number the last operation to the file was performed.
ERRMSGID Error mention encountered when last operation was performed. Blank = no error.
FILE Name of file.
LIBRARY Library file is in.
MEMBER File member name.
RCDFORMAT Record format.

Now we can look at the RPGLE/RPG IV code:

01 FTESTPF    IF   E           K DISK    extmbr('*ALL') 
02 F                                       infds(FileDs)

03 D FileDs        E DS                  extname(FILEDS)
   D                                       qualified
    /free
04    dow (1 = 1) ;
05      read TESTPFR ;
06      if (%eof) ;
07        leave ;
08      endif ;

09      post TESTPF ;
        .
        .
10    enddo ;

11    *inlr = *on ;

In the F-spec, line 1, is where I used the EXTMBR to define that I want to read all members. And on line 2, I have defined the File Information Data Structure for this file will be FileDs using the INFDS keyword.

By using the external data structure for the File Information Data Structure definition it can be simply be added by defining the file name of the file containing the external data structure definition in the EXTNAME keyword, see line 3. I have also QUALIFIED the data structure sub fields.

Line 4 - 8 are straight forward and I am not going to explain.

To load information into the File Information Data Structure I need to use the POST operation code, line 9. It is followed by the file name so that it knows which file's information to load into which data structure.

So what happens when this program runs:

FLD1 FileDs.Member
1 1 ONE
1 2 ONE
1 3 ONE
1 4 ONE
1 5 ONE
0 0 TWO
2 1 TWO
2 2 TWO
2 3 TWO
2 4 TWO
2 5 TWO

If I ran RDMBRALL1 in debug and looked at the values in FileDs after the first read of TESTPF it would look like this (I am excluding the Filler fields):

 > EVAL fileds
   FILEDS.FILEOPEN = '1'
   FILEDS.ENDOFFILE = '0'
   FILEDS.FILESTATUS = '00000'
   FILEDS.OPCODE = 'POST F'
   FILEDS.ROUTINE = '*DETC   '
   FILEDS.SRCSTMTNBR = '00001500'
   FILEDS.ERRMSGID = '       '
   FILEDS.FILE = 'TESTPF    '
   FILEDS.LIBRARY = 'MYLIB     '
   FILEDS.MEMBER = 'ONE       '
   FILEDS.RCDFORMAT = 'TESTPFR   '

From this simple example you can see how easy it would be to identify which member is being read, answering the fourth question posted at the start of this post.

More information about these commands, operation codes, etc. can be found on the IBM website.

In my post about Externally described Data Structures someone asked why I did not use just a /COPY or /INCLUDE to insert that data structure into the program's source. For that situation that was a valid question and either way would have worked.

I believe that in the scenario of wanting the File Information Data Structure on two files in the same program then the external data structure is easier, as shown below:

01 FTESTPF1   IF   E           K DISK    infds(File1Ds)
02 FTESTPF2   IF   E           K DISK    infds(File2Ds)

03 D File1Ds       E DS                  extname(FILEDS)
                                           qualified
04 D File2Ds       E DS                  extname(FILEDS)
                                           qualified

If I was to use the /COPY or /INCLUDE would I need two pieces of code to copy, one for each data structure?

 

This article was written for IBM i 7.1, and it should work with earlier releases too.

10 comments:

  1. Multi-member files have been supported since the S/38 days and can be useful. But I wanted to point out that SQL tables are single member files (except for partitioned tables). SQL does have limited support for access to specific members, such as CREATE ALIAS. The SQL aspect may be something to consider when designing applications.

    Karl Hanson (posting anonymous, as I'm unfamiliar with blog procedures)

    ReplyDelete
    Replies
    1. Karl;
      To post as your own name click on the drop down arrow next to where it says either 'Comment as' or 'Reply as'.
      Select NameURL.
      In the pop up fields enter your name in the Name field. URL is optional.
      Click continue & the comment will have your name as the poster, rather than Anonymous.

      Delete
  2. The easyest way is to use a partitoned table, it is the same as a multi member PF, but separated by key values.

    ReplyDelete
    Replies
    1. Can you given an example of how you would do it?

      Delete
  3. Simon -

    Please note that when processing overriding to *ALL members that the file members are NOT processed in their alphabetic name order - they are processed in the order that they were /added/ to the physical file.

    For example:
    If the file being in the RPG program has two members, and member TWO was added to the file first, the records for member TWO will be processed first, followed by the records in member ONE.

    ReplyDelete
  4. Thanks for sharing.

    ReplyDelete
  5. Thanks for the detailed explanation..

    ReplyDelete
  6. Isn't it easier to just create an LF with dtambrs *all?

    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.