The (More) Modern RPG Language
December 16, 2019 Bob Cozzi
Back in 1988, I wrote what became the book on RPG III. Then in 1996, I published the RPG IV version and updated it again circa 2000. But in the years that followed, RPG IV became mostly stale; a tweak here and there, but nothing too spectacular.
In recent years, a wave of RPG IV enhances has been revealed, most notably free-format was completed and helped propel RPG IV, once again into a truly modern language. Although the measure of “modern” for RPG IV seems to lean toward how much free-format syntax is supported; which is ironic considering COBOL and FORTRAN were always free format and around years before Neal and Buzz walked on the Moon over 50 years ago.
Today, RPG IV is a good choice for development on our favorite platform and has just about everything we need to get the job done. With the improvements to embedded SQL in RPG IV in recent years, it is hard to justify using anything but RPG and SQL.
Top Improvements to RPG IV
Free-format support was completed back in the v7r1 era and is clearly the most significant improvement to RPG since RPG IV was announced. There have been many other important new features introduced into the language in recent years that make RPG IV even better. Let’s look at my top 10 favorite new RPG IV features.
Maximum Field Length 16 Megabytes
RPG II and the original RPGIII had a 256-byte maximum field length. Today, fixed and varying length fields in RPG IV can be up to 16 megabytes. No longer limited to the original RPG IV 32k and more recent 64k lengths, developers can stop thinking about how to trick the compiler into using more than 64k of storage.
Back in the 1990s, I “invented” dynamic array support for RPG IV. I used User Spaces with the “auto extend” attribute and mapped an RPG IV Array to the User Space using RPG IV pointers. This worked fantastic for 20 years. Today, RPG IV has Dynamic Array support built-in. I have no idea how they do it within the compiler itself (probably not user spaces) but it works. The syntax is only slightly more complex that using a normal array and my original technique.
SQL LOB Support
While clearly not a strict RPG IV language improvement, support for the LOB (Large Object) SQL data type is key to today and tomorrow’s web applications. Sending and receiving web content can certainly fit within the 16 MB limit of RPG IV fields, however we do not know what tomorrow will bring except larger values. The IBM-provided web services or whatever we’re calling it today, that are SQL UDFs (User Defined Functions) and UDTFs (User Defined Table Functions) return Large Object (LOB) data. This data can potentially grow to several Gigabytes and RPG IV LOB support is key to handling it.
The SQLTYPE(SQL_CLOB) keyword on a DCL-S (Declare) statement causes the precompile to generate code to handle LOB data. Developers can direct data to that variable from an SQL statement’s INTO clause. To push that data directly out to the IFS is easy with the SQLTYPE(SQL_FILE) keyword that causes the CLOB or BLOB to be saved to the IFS automagically. There is also the GET_CLOB_FROM_FILE() SQL function that pretty much does what its name implies.
A CLOB can be moved into an RPG VARCHAR or fixed-length field using the SQL VALUES or SQL SET statements. Typically you would use the SQL SUBSTR() function to extract a piece of the CLOB unless it is relatively small, after all would you really want to copy 45 Megabytes? A piece at a time would be a more practical approach. On IBM i, LOBs tend to be mapped to LOB Locators. These are essentially a handle to where the LOB content is stored. So, when the LOB Locator is specified on the SUBSTR, the system is passing a 4-byte integer that contains a number which the SQL engine uses to go fetch the number of bytes requested. This allows RPG and SQL to avoid moving huge chunks of data around.
When you run something like EXEC SQL VALUES SUBSTR( :UPSLABEL, 1, 500) INTO :LAB1; the LOB Locator is actually being referenced in order to fetch the 500 bytes of the UPSLABEL content. Much better than moving the entire multi-kilobyte or multi-megabyte dataset all at once.
While technically this may be a DB2 for i feature, it certainly opens up a lot of cool features to RPG IV.
For decades C++ has had what is called function overloading: The ability to specify a function (a.k.a. subprocedure) and provide both default parameters and alternative parameter choices. Overloading gives you the ability to write a subprocedure named XYZ with three parameters, and then write a second version of XYZ with two or three or four or whatever number of parameters you want. Unlike optional parameters, this support gives uses duplicate subprocedure names to handle different parameter types. The compiler routes inline calls to the correct subprocedure based on the name and the data-type of the parameter passed to the subprocedure.
RPG IV support for procedure overloading uses a little different syntax, you can’t have duplicate procedure names in RPG IV, so it requires that each procedure have a unique name (the customary standard is to add a suffix to the procedure names with the parameter type or parameter name as the variation) and then use the OVERLOAD keyword to group the various procedure names into one Overloaded procedure. Provided the returned value is consistent, you specify the OVERLOAD keyword on a separate Prototype with each associated procedure name.
The RPG IV Overloaded procedure syntax is kind of unique, but it is “RPG-like” and I’m glad to have.
A low-profile enhancement in recent years is ON-EXIT. This oddly simple opcode (it has no parameters and has no closing “END-EXIT” partner) is placed at the bottom of your mainline calcs or at the end of a subprocedure or both. What happens is this: regardless of where or how the program or subprocedure ends, the code following on-exit opcode is called. If you’ve opened a file, you can make sure it gets closed by added the CLOSE opcode after the ON-EXIT. If you allocated memory, you can make use you free or dealloc it.
When I first saw ON-EXIT, it was a little too simple and I thought there must be a more complicate implementation that I was missing. I was happily surprised that it turned out to be just that simple . . . add the ON-EXIT and code the stuff you want to make sure happens when the routine ends. This doesn’t strictly need to be error code or “just in case” stuff. The normal tasks such as closing SQL cursors, closing Print or database files, even deallocating memory may occur after the ON-EXIT. Adding ON-EXIT simply ensures that those routines will run when the program ends, no matter how it ends.
Long Field Names
When RPG IV first came out some 25 years ago, 10-character field names seemed huge compared to the pathetically short six-character names of RPG III. Jump ahead to today where RPG IV has, arguably the longest field name support of any programming language. RPG IV supports field and other identifier names of up to 4096-bytes. For fields, arrays and data structures, these are 4K of unique names, for subprocedures the limit is something around 255. While some other languages claim long name support, most are limited to the first 16 or 32 characters being unique while the rest of the name is ignored. RPG IV wins on field name length.
Built-In EBCDIC to ASCII Conversion
What, you didn’t know this? When you declare a field in RPG IV you can specify the CCSID keyword along with the CCSID value. For example:
DCL-S weboutput varchar(200) ccsid(1208);
This causes the compile to believe that this field contains ASCII data. Without the CCSID keyword, the compiler believes the field contains data in the Job CCSID that was in use when the program was compiled.
Now let’s do a classic conversion. Let’s convert classic North American EBCDIC (a.k.a. CCSID(37)) to UTF-8 ASCII (a.k.a. CCSID(1208)). To do that the following “complex” set of instructions needs to be performed:
weboutput = ‘Hello World’;
That’s it! RPG IV automatically converts data to the target field’s CCSID. Since I specified CCSID UTF-8 for the WEBOUTPUT field, the EBCDIC literal is converted during the implied EVAL operation. Likewise, you can convert from ASCII to EBCDIC just as easily; for example, data received from the web.
I find that most apps do not specify the CCSID for the input values from web services such as HTTPPOSTCLOB() or similar. So the ASCII data may be stored as ASCII within an EBCDIC field. There is a work around that I came up with for this situation. It requires pointers so skip ahead if you’re pointer phobic.
Dcl-s webdata char(500) ccsid(1208) based( pEBCDIC ); Dcl-s rtnBuffer char(500); Dcl-s pEBCDIC pointer inz(%ADDR(rtnBuffer); Dcl-s clearData char(500); // call web services and receive ASCII data in the RTNBUFFER field. EXEC SQL select responseData into :rtnBuffer FROM table(httpPOSTCLOB( blah, blah blah); clearData = webData; // Copy the ASCII content to an EBCDIC field.
The call to the web service routine returns data into the RTNBUFFER field. While the data in RTNBUFFER is ASCII, the compiler thinks it is EBCDIC because there’s no CCSID keyword on it. Moving RTNBUFFER to another field wouldn’t force a CCSID conversion. Moving it to an ASCII field would convert the ASCII into ASCII and since it thinks it is EBCDIC it would produce junk.
To convert it properly we could call the iconv() function, but with RPG IV we no longer need to do that. I’ve mapped the WEBDATA field that has CCSID(1208) to the RTNBUFFER field. Think of it as you would two data structure subfields occupying the same positions. Perhaps one is Packed, the other is Character. I’m overlapping the EBCDIC field with an ASCII field so RPG sees the ASCII field’s content as ASCII. When I move that field to another field CLEARDATA in my example, the compiler generates the conversion for me, all I had to do was the move (EVAL).
Load And Save IFS Files
While it is a combined SQL and RPG IV enhancement it is worth mentioning. Today RPG IV has no native support for the IFS except for the /INCLUDE statement. However, if you add SQLTYPE(CLOB_FILE) to a variable you can use SQL to save and restore IFS file content into your RPG program. This support is much easier than using the open/read/write/close IFS APIs and a lot more fun.
Almost Totally Free Format
While it might seem an easy pick, RPG IV has been mostly free-format for decades, however now that the H and D specs have joined the C specs as free format, you can write a program without resorting to fixed-format columns, save program-described output specs. In fact, since mid-V7R1 you no longer need to specify the /FREE /END-FREE statements… just leave columns 6 and 7 blank and use 8 to 80 for your “totally free format” statements. Need to use columns 1 to 5 or 1 to 7 like other languages? Then insert the *FREE statement into line 1, position 1, and it opens up those extra 7 columns to RPG code.
While Input and Output Specs are still considered legacy and not supported in free format, I wouldn’t be surprised if at some point Output Specs get a somewhat free format make-over for non-Database file output (e.g., Print).
Embedded SQL As An RPG IV Extension
I rarely create RPG IV source members with SRCTYPE(RPGLE). I nearly always create them with SQLRPGLE. Even if I’m using traditional File I/O the added capability of SQLRPGLE is like opening up an entire catalog of interfaces to the language.
- Convert to all upper case:
- EXEC SQL VALUES UPPER( :CSTNAME ) INTO :CSTNAME;
- Convert a Date into some cool format:
- EXEC SQL VALUES VARCHAR_FORMAT(:INVDATE, ‘DD Mon YYYY’)
- EXEC SQL VALUES VARCHAR_FORMAT(:INVDATE, ‘DD Mon YYYY’)
- Get the Name of the Day of the week:
- EXEC SQL VALUES DAYNAME(CURRENT_DATE) INTO :TODAY;
- Use IBM, third-Party, and in-house User Defined Functions
And the list goes on. . . .
Clearly anyone can see from these features, that RPG IV has become a powerhouse language, and when you include the huge library of capability that embedded SQL brings to the table, you’ll find RPG IV hard to beat. RPG IV developers today have a lot of cool features to choose from, but knowing what features are available can be challenging. Hopefully in some small way I have modernized your RPG IV knowledge of the cool features and functions RPG IV has been given over the last several years. Let me know what your favorite new RPG IV features are. You can reach me on Twitter @SQLiQuery and @BobCozzi or via email firstname.lastname@example.org.