The latest V5R2 RPG IV enhancements include some keywords on the file specification that go a long way toward closing the gap on encapsulating file processing to the lowest level of programming, the subprocedure. The new enhancement stops just short of this, allowing generalization down to the module level. But it gives us hope that we'll soon be able to code independent subprocedures around file processing without globalization of the file declarations themselves.
This newfound independence is garnered from two file specification keywords that are new as of V5: EXTFILE and EXTMBR. These keywords are similar to the OVRDBF command's TOFILE and TOMBR parameters, respectively.
EXTFILE accepts a case-sensitive filename as its value. The filename is case-sensitive for the purposes of providing support for filenames outside of the DB2/400 environment. It is important to note that all DB2/400 file and member names are uppercase. EXTFILE can take any of the following forms:
- A variable, such as DB2VariableName
Example: F MyFile if e k disk EXTFILE(DB2VariableName)
- A literal value, such as 'DB2FILENAME'
Example: F MyFile if e k disk EXTFILE('DB2FILENAME')
- A qualified literal value, such as 'DB2LIBNAME/DB2FILENAME'
Example: F MyFile if e k disk EXTFILE('DB2LIBNAME/
- A qualified literal that uses the default library list value, such as '*LIBL/ DB2FILENAME'
Example: F MyFile if e k disk EXTFILE('*LIBL/
Where DB2VariableName is a DB2/400 filename of the format LIBNAME/FILENAME, LIBNAME is either the name of the DB2/400 library or the default value *LIBL, and FILENAME is the name of the DB2/400 file that is being overridden. Therefore, the value of the variable DB2VariableName could be any one of the three values in the EXTFILE examples listed above.
- A variable, such as DB2Member
Example: F MyFile if e k disk EXTMBR(DB2Member)
- A literal value enclosed in single quotes, such as 'MyMember'
Example: F MyFile if e k disk EXTMBR('MyMember')
- The default value of *FIRST
Example: F MyFile if e k disk EXTMBR('*FIRST')
- The literal representing all of the file members, *ALL'
Example: F MyFile if e k disk EXTMBR('*ALL')
Note that use of EXTFILE and EXTMBR are independent of one another in the same manner as the TOFILE and TOMBR parameters on OVRDBF. If you wanted to override the file MyFile2 to DB2 filename PARTS in library ACCT using all of the members, then you would code the following file specifications:
Normally, I would code the EXTFILE and EXTMBR keywords on the same line, but I generally code the USROPN keyword on a separate line for clarity.
If any file overrides occur to the files referred to by the RPG IV program, then those overrides take effect during program execution. This might seem confusing, so let's look at a couple of examples. If the previous override to MyFile2 exists in program TXR071, then what happens when the code in Figure 1 gets executed?
Figure 1: Use these code fragments for testing EXTFILE and EXTMBR keywords.
The lines at section A in the figure will accomplish nothing in program TXR071 because the file specification actually affects a file named ACCT/PARTS, not MyFile2. MyFile2 is a dummy name in this sample RPG IV program. The real file name is the name assigned by the EXTFILE keyword.
The code at section B will override the file. But what's this? It's overriding the file to a file of the same name as before. Ah, but the member name on the TOMBR parameter is different from that of the EXTMBR keyword. Yes, instead of using the *ALL members that is listed on the EXTMBR keyword, this code uses the TOMBR value of *FIRST and only executes against the first member of the ACCT/PARTS file.
Section C is similar to section A in that it is not truly using any file called MyFile2. Realize however, that if there were such a file on the system, then the OVRDBF would affect the program file of the same name.
Keep reminding yourself that DB2/400 filenames are in uppercase. The names MYFILE2, myfile2, and MyFile2 are the names of three different files on the EXTFILE keyword. If you use either of the last two, an error will be generated; the program will not locate a DB2/400 file with the name myfile2 or MyFile2. Especially keep this in mind for those programs in which end- users may be keying in the filename. Turn on the "uppercase only" flag in your DDS specifications for the input field!
This is a style issue or perhaps a shop standard issue, but I recommend using the EXTFILE/EXTMBR keywords in conjunction with the USROPN keyword. User-controlled files are not used as often as they should be. The time has come to get friendly with this keyword if you're not already. This also allows a generalization of the EXTFILE/EXTMBR keywords to the module level. At this point, an explanation of what I mean by "generalization" is in order.
In an object-oriented world, it is preferable to be able to reference files within subprocedures related to those files. For example, wouldn't it be nice to be able to load an array with numeric data from a variety of accounting files? Most of the accounting fields would have a similar size (or at least a maximum that you'd be aware of). Wouldn't it be great to have a subprocedure where you pass in a filename--perhaps a field name or field position where the assigned field name could be looked up in a table--and have an array of data values returned for that file? Consider, for example, a year-end summary where data for the previous 12 months could be gathered and returned. Data in the subprocedure could open the file, read the monthly data, summarize the data, place the values in buckets for each month, close the file, and return the array to the calling program. This could be done via a call such as SumMonths = Summary('GLAcct':'GLPER'), where the subprocedure Summary and the variable SumMonths are both arrays, 'GlAcct' is the name of the file, and 'GLPER' is the name for the key used to gather the data. In the subprocedure, two files are actually opened: 'GLAcct' for reasons previously stated and another file, let's call it CompanyTbl, that associates the keys 'GLAcct'/'GLPER' with summing a particular series of fields. In fact, you could even code this directly in the subprocedure without the support of a CompanyTbl if you wanted to.
Maybe one day, this will become a reality. For now, it's just a dream. But what we do have is generalization down to a module level. In other words, we currently can't place a file specification into a subprocedure, but we can place it in a module. As such, we can have a module (*MODULE) or service program (*SRVPGM) that supports the passing of filenames and member names as parameters while executing in a fashion similar to the generalized subprocedure discussed above. The first two parameters are part of the procedure interface for an EXTPGM that passes those parameters. A third parameter is required to pass back the SumMonths array values unless a global variable or IMPORT/EXPORT is used.
Speaking of IMPORT/EXPORT, the variables used for the filenames and member names can be IMPORTed and EXPORTed from modules. However, I would avoid this because it has a tendency to destroy the generality of the code. If you know what your filename is, why not use it in the main RPG program's file specifications? Chances are, if you are using it for IMPORT/EXPORT, you need it for more than just your routines, so why not code it where it will be global? Still, you should be aware that this option is available to you.
Also, the variable can be set in the initialization subroutine (*INZSR) or in the D-spec on the INZ keyword. The same arguments I have about IMPORT/EXPORT lead me to frown on these options as well.
Generalize your programs by taking advantage of the usefulness of these keywords with files that have similar file structures. If you specifically know your filename, use a quoted literal on the EXTFILE/EXTMBR parameters along with USROPN. This may eliminate the need for a calling CL program. Use the *INZSR to initialize the field only if the value is passed as a parameter to the main program on its procedure interface. Give your particular situation some serious thought before coding these keywords. Many of our shops have multiple files that have either the same field names or field names in which one letter differs from file to file. This most often occurs because more than one of these files is needed in several programs. These are highly likely candidates to code EXTFILE routines around.
RPG has come a long way as a language, surviving numerous "it's dead" challenges. RPG IV is a much different creature than any prior incarnation of the language. Though the file specification hasn't changed much, it has changed for the better. Now, it's up to the programmers to start coding for the future with this specification. Personally, I think my dream for a general availability of file opens down to the subprocedure level will become a reality. But for now, I love the capabilities that I have in hand. I hope this article has been a revelation for some and an encouragement to all!