Sharing In V6
March 21, 2012 Jon Paris
Some time ago I read Ted Holt’s Guru tip Sharing Simplifies Source Code in which he showed an approach to building reports in a modular programming environment. At the time I can remember thinking that it would be interesting to see how V6 RPG’s ability to pass files as parameters might offer an even simpler approach.
Simpler, that is, once you know how to pass files as parameters! Well, it has taken me almost a year, but I finally got round to working on it, and I confess I rather like the results.
Ted’s original approach required that a driver CL program be written that would perform an override on the print file so that its open data path (ODP) could be shared. The CL also called each of the programs in turn and then deleted the override. This new approach does away with the CL program because it does not require an override. You simply call the initial report program and it can then call as many secondary programs as and when it needs. In fact, it offers an additional benefit in that a secondary program can be called to add data to the report at any point in the process and as often as is required. That really does add reporting power to a modular application.
Let’s begin with the changes needed to the main program (AR101R) to allow it to pass the printer file to the secondary program (AR102R). Here are the two additional pieces of code required. First is the prototype for calling the secondary report program.
D PrintRecap Pr ExtPgm('AR102R') D printFile LikeFile(AR100P) D overFlow n
The first parameter is the file itself. Notice that it is defined with the new (V6) LIKEFILE keyword and references the name used in the original F-spec shown here:
FAR100P o e printer OflInd(overFlow)
The second parameter is the indicator associated with the F-spec OFLIND keyword for the file. When we pass a file as a parameter, RPG links up all of the “under the covers” stuff for us, but any items such as the overflow flag that we need to reference in our logic must be passed explicitly.
Once the prototype is in place, all that remains is to add the call logic in the appropriate spot.
The file is passed simply by specifying its name as the first parameter, and the field associated with the OFLIND keyword is passed as the second parameter. Those are the only changes we need.
Things are a little more complicated in the called program, but as you’ll see it is all quite straightforward. Here’s the full program:
Far100p o e printer Template D PrintRecap Pr ExtPgm('AR102R') D printFile LikeFile(AR100P) D overFlow n D PrintRecap PI D printFile LikeFile(AR100P) D overFlow n D recap01DS ds LikeRec(PrintFile.Recap01: *Output) D pageHdrDS ds LikeRec(PrintFile.PageHdr: *Output) D recapData ds inz Qualified D order# 5 0 D total 9 2 /free exec sql declare recap cursor for SELECT ORDER#, sum(ORDQTY * PRICE) FROM SQLBASE/ORDERITM GROUP BY ORDER#; exec sql open recap; write PrintFile.PageHdr pageHdrDS; exec sql fetch recap into :recapData; dou SqlState >= '02000'; if Overflow; write PrintFile.PageHdr pageHdrDS; Overflow = *off; endif; eval-corr recap01DS = recapData; write PrintFile.Recap01 recap01DS; exec sql fetch Recap into :recapData; enddo; return;
The first thing to notice is that the F-spec entry uses the keyword TEMPLATE. This tells the compiler that we want to be able to use the file’s details (record formats, fields, etc.) but not to open the file in the program. In fact the only reason I even defined it is so that I could use the LIKEFILE keyword on the procedure interface (PI).
One other thing to note is that I had to define two data structures recap01DS and pageHdrDS. These are based (LIKEREC) on the print file record formats that I want to use. This is necessary because when a file is passed as a parameter, no I or O specs are generated and therefore all I/O operations must be made using the Result DS method. Note that this is required even in cases such as the page header (pageHdrDS), where there are no variables in the record format. I still had to specify the appropriate DS or the compiler got upset.
The write operations for the printer exhibit another small difference that might be new to you. Because the file was specified using the LIKEFILE keyword, all references to it have to be qualified. In other words, instead of simply coding like this:
write PageHdr pageHdrDS;
To write the page headings I have to qualify the record format to the file name, as follows:
write PrintFile.PageHdr pageHdrDS;
Personally I rather like that–particularly in programs that use multiple files as it makes it clear which file is being written to without having to know which record format relates to which file.
In trying to keep the example code close to Ted’s original, I also used embedded SQL in the recap program. That highlighted a deficiency in the SQL pre-compiler’s handling of LIKEREC data structures. According to the manual, and my experience bears this out, LikeRec structures are only allowed as host variables if they don’t have the second parameter specified (i.e., the *Input, *Output, *All option). Since in most cases the RPG compiler doesn’t like to use them in result field I/O operations unless that second parameter is present, we have ourselves a Catch-22 situation.
In this case I got around it by coding a DS (recapData) to hold the host variables, which made SQL happy, and then used EVAL-CORR to copy the fields to the output record DS, which made RPG happy. In my opinion this shouldn’t be necessary as the two structures are identical in every way, but right now we seem to be stuck with the limitation.
As far as the coding differences go that is all there is to it. But there are a couple of benefits to using this approach that I think are worth highlighting:
All of this while maintaining Ted’s objective of not having to add complexity to the original report to add the recap data. Having explored this technique, it is certainly one that I will be using in the future.
Jon Paris is one of the world’s most knowledgeable experts on programming on the System i platform. Paris cut his teeth on the System/38 way back when, and in 1987 he joined IBM’s Toronto software lab to work on the COBOL compilers for the System/38 and System/36. He also worked on the creation of the COBOL/400 compilers for the original AS/400s back in 1988, and was one of the key developers behind RPG IV and the CODE/400 development tool. In 1998, he left IBM to start his own education and training firm, a job he does to this day with his wife, Susan Gantner–also an expert in System i programming. Paris and Gantner, along with Paul Tuohy and Skip Marchesani, are co-founders of System i Developer, which hosts the new RPG & DB2 Summit conference. Send your questions or comments for Jon to Ted Holt via the IT Jungle Contact page.