The idea for this post came from a discussion I saw on Facebook. Someone asked:
- In your RPG how can you read from a particular member of a file?
- By default which member is read?
- How can you read all the members of your PF in your RPG program?
- 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.
- OVRDBF command
- EXTMBR RPGLE File specification keyword
- File information data structure
- INFDS file feedback example
- RPGLE POST operation code
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.
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.
ReplyDeleteKarl Hanson (posting anonymous, as I'm unfamiliar with blog procedures)
Karl;
DeleteTo 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.
The easyest way is to use a partitoned table, it is the same as a multi member PF, but separated by key values.
ReplyDeleteCan you given an example of how you would do it?
DeleteSimon -
ReplyDeletePlease 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.
Thanks for sharing
DeleteThanks for sharing.
ReplyDeleteThanks for the detailed explanation..
ReplyDeleteIsn't it easier to just create an LF with dtambrs *all?
ReplyDeleteHow to use CHAIN for multi member PF?
ReplyDeleteCHAIN = SETLL + READ
DeleteAll members will have the same key.
You just need to make sure that the EXTMBR keyword has the appropriate member name in it.
Is it possible to change the sequence of the members of a file. By default the oldest member is the first member.
ReplyDeleteIs there a way to change the some other member to be a default member.
Thanks
Alas, no the members are included in date created order.
DeleteHow to read 2, 4, 6, 8 corresponding member in PF? Kindly let me know.
ReplyDeleteYou ought to read this.
DeleteHi Simon, I have multimember file,File have 12 members i need to read 4 member 12th record how can i write code. Could you please explain Thank you advance.
ReplyDeleteYou would use the EXTMBR to denote which member you want to open, see here.
DeleteAs for reading the 12th record you should be able to do that on your own.
Thank you Simon !
DeleteHi Simon
ReplyDeleteI want to read member 4 and member 10 in different subroutines.
Do i need to have a variable in extmbr keyword. Then update the keyword
before reading the file is it enough
This can be achieved in two ways:
Delete1. Use a variable name in the EXTMBR for example
DCL-F FILE EXTMBR(Variable) USROPN ;
Move the first member name to Variable.
Open the file.
When done close the file.
Move the second member name to Variable.
Open the file.
When done close the file.
2. Define the file twice:
DCL-F FILE1 EXTFILE(FILE) EXTMBR('FIRSTMBR') RENAME( : FILE1R) ;
DCL-F FILE2 EXTFILE(FILE) EXTMBR('SECONDMBR') RENAME( : FILE2R) ;
Then you can use both members without having to open and close them.
Perfect.. Thanks
DeleteWhat is the need of multimember? Can you pls explain with example.
ReplyDeleteIn my opinion multi-member files should not be used.
DeleteI have worked with too many companies who think they are being smart using different members for things like different companies, and then come to regret it.
For data my recommendation it is good database design to have only one member in every data file.
Source files... well.. you need those to be multi-member.