Tag Archives: RPG

Using EXTFILE to override files within RPG

So here’s the situation: Five warehouses populating five sets of files (same filenames, different libraries) and I have an RPGLE program that needs to read through each of these to accumulate dispatch information.

The traditional way of doing this would involve writing a CL program to OVRDBF to each file before calling the RPGLE program. This works, but it’s a bit messy and this can cause future maintenance problems. It’s much better, therefore, to specify the file (and library) to open within the RPGLE program itself. You can do this with the EXTFILE keyword.

Here’s an example:

 * ---------------------------------------------------------------------- *
 * Program     : LSX001R                                                  *
 * Description : Dynamically select which file to open                    *
 * ---------------------------------------------------------------------- *
h main(Main)
fMOVEMENTS if   e           k DISK    usropn extfile(filename)

d Main            pr                  extpgm('LSX001R')
d  library                      10a

dfilename         s             21a

 * ------------------------------------------------------------------------
 * Main Procedure
 * ------------------------------------------------------------------------
p Main            b
d Main            pi
d  library                      10a
 /free

     filename = %trim(library) + '/' + 'MOVEMENTS';
     open MOVEMENTS;

     // Do whatever processing on the file needs to be done...

     close MOVEMENTS;

 /end-free
p Main            e

Hopefully this is reasonably straightforward. In the file spec for the MOVEMENTS file, I have used EXTFILE with a variable filename to specify the actual file opened. I have also used the USROPN keyword as I need to populate the filename variable before I attempt to open the file.

Populating this field is pretty simple. I pass the library name to the program as a parameter and the first thing the Main procedure does is concatenate the library and file with a ‘/’ separator.

Now I can open the correct file, do whatever processing is necessary, and then close the file afterwards.

A similar approach, using the EXTMBR keyword can also be used to open a specific file member. You can, of course, combine EXTFILE and EXTMBR in order to dynamically determine both the file and member, if you really want to.

It should be noted that this will only work if you are using traditional database IO. Any embedded SQL will remain unaffected by anything you do in the F-Spec. If you want to do something similar with SQL, you will need to look into the CREATE ALIAS statement.

Flattr this!

Happy Birthday, RPG IV

RPGPGM.com notes that 21 years ago today, RPG IV (also known as RPG ILE) was released.

The first version of RPG IV came as part of OS400 V3R1, which was released on November 25 1994. Even though the code was still constrained by columns, the new Definition specifications (D-specs) was introduced, and I could now use variable names that were up to ten characters long. It also made it possible to use Date, Time, and Timestamp data types along with operations codes to be able to easily perform math with them. And I no longer had to use indicators for reads, chains, etc.

This makes me feel a bit old as I remember when RPG ILE was the next big thing. And, without much fanfare, it has continued to evolve — in many ways more rapidly than other, more popular languages. This is both RPG’s greatest strength and its greatest weakness.

IBM have, over the years, put a great deal of effort into ensuring that, for all the new features, backward compatibility has never been broken. This means that if you are running an i on Power, you can upgrade as and when you are ready without having to worry about all of your applications breaking. There are not many reasons for not staying on a supported release, and fewer valid valid ones.

The downside of this is that there is nothing to force developers to take advantage of any of these new features. You could, if you wanted to, write programs in exactly the way you did 30 years ago. They would still compile, still run and still work as expected. And people do.

RPG today is in the somewhat unfortunate position of being a very modern language that is often perceived as being old fashioned. This is because it lets programmers get into a rut and never learn anything new.

I doubt that there is any way of squaring this particular circle. Backward compatibility is important and business critical applications shouldn’t be put at risk. But remember this: If someone claims that RPG is old, obsolete, or worse, the issue to which they are referring is not a technical one.

Flattr this!

Converting dates to numbers in RPG

I keep encountering situations where I have to write a record to some painfully old file that stores a date as an integer. Converting a date to an integer is one of those tasks that is very simple to accomplish but which I never remember how to do.

So, for future reference, here is the command for converting the current date to a seven digit integer (cyymmdd format), wrapped in a bit of code that displays the results because we all like to know what we’re writing befor we write it.

H                                                                          
D DateNum         s              7s 0                                      
 /Free                                                                     
                                                                           
     DateNum = %dec(%char(%date():*cymd0):7:0);                            
     dsply DateNum;                                                        
                                                                           
     *INLR = *ON;                                                          
                                                                           
 /End-Free                                                                 

Flattr this!

Vim syntax highlighting for the IBM i

As you may have noticed, some of the personal projects I have mentioned on here have been written for the IBM i. I generally develop these locally in Vim and then copy the source files to a server so that I can compile and test them as and when I have a chance.

I like Vim. When I want to just sit down and get something down, Vim provides a distraction-free environment with a powerful collection of features. One of these features is syntax highlighting, and Vim comes with syntax files for a host of languages as standard. Unfortunately, RPG is not one of the languages supported out of the box.

Inevitably, however, it didn’t take much time with Google to discover that someone else had the same thought as me some time ago. So I must say thank you to Martin Rowe of DBG/400 for this rather gorgeous collection of syntax files.

Flattr this!

Regular expressions in RPG

I learned something new today. Regular expressions are something I have used quite frequently on my Linux laptop at home but I hadn’t realised that they are natively supported in the IBM i ILE environment.

Thanks to Scott Klement at iProDeveloper, now I know.

This is a useful and powerful feature and one for which I can see many real-world applications. As soon as an opportunity presents itself, I shall be returning to Scott’s article, downloading the code bundle and taking full advantage of this functionality.

Flattr this!

Unduplicating duplicate lines with SQL and RPG

This is probably a very specific issue but I’m quite proud of the solution. And it may prove to be useful to someone else (or me at some other point) so I’m putting it here for posterity.

The issue

We receive a list of sales transactions through an EDI interface. With the implementation of a new retail management system, these transactions now need to be pointed in the direction of the new application. This has proved interesting, on occasion, as the existing system is a lot more forgiving of incomplete data than the new one.

One of these cases, we have discovered, is that for some stores we’re not receiving transaction numbers. We have dates, times, store codes, transaction line numbers – but no transaction numbers. This means that if a store manages to enter two transactions at two tills at exactly the same time, we get a whole bunch of duplicate lines in the interface extract.

This interface is a transitional one (once the roll-out is completed for all stores, it will be redundant) so I shouldn’t be spending too much time on it. Just enough to make it work.

The Solution

Putting together an SQL statement that identifies transactions with duplicate line numbers is pretty easy, as is constructing a statement to update records based on a fixed criteria. The question is, how easy is it to construct an SQL statement to quickly update records based on a dynamic criteria.

The answer is: easier than I expected.

The Implementation

The (slightly simplified) code, is pasted below without explanation but if any part of it is unclear do feel free to either leave a comment or contact me and I will be happy to clarify.

     H
      *------------------------------------------------------------------------*
      * Program     :                                                          *
      * Description : Clean up duplicate lines                                 *
      * Written by  : Paul Pritchard                                           *
      * Date        :  2/07/2012                                               *
      *------------------------------------------------------------------------*
     D pDuplicate      DS                  QUALIFIED
     D  Date                         10A
     D  Time                          8A
     D  Store                         5S 0
     D  Register                      3S 0
     D  Transaction                   5S 0
     D  Line                          3S 0
     D*
     D SaveDate        S             10A
     D SaveTime        S              8A
     D SaveStore       S              5S 0
     D SaveReg         S              3S 0
     D SaveTxn         S              5S 0
     D SaveLine        S              3S 0
     D*
     D NewLine         S              3S 0
      *------------------------------------------------------------------------*
      /free

          // The Commitment control workaround
          exec sql Set Option Commit = *NONE;

          // Renumber any duplicate lines in the Sales File
          exsr srDuplicate;

          // And exit
          *INLR = *ON;

      /end-free
      *------------------------------------------------------------------------*
      * Subroutine: Renumber any duplicate lines in the Sales interface file   *
      *------------------------------------------------------------------------*
      /free

          begsr srDuplicate;

              // Initialise the work values
              exsr srReset;

              // Find the lines with duplicate numbers
              exec sql declare cSales cursor for
                  with
                      Duplication as
                      (select wrslsdate, wrslstime, wrslsistr, wrslsreg#,
                              wrslstxn#, wrslslin#, count(*)
                       from SALES
                       group by wrslsdate, wrslstime, wrslsistr, wrslsreg#,
                                wrslstxn#, wrslslin#
                       having count(*) > 1
                       order by wrslsdate, wrslstime, wrslsistr, wrslsreg#,
                                wrslstxn#, wrslslin#)
                  select wrslsdate, wrslstime, wrslsistr, wrslsreg#, wrslstxn#,
                         wrslslin#
                  from SALES
                  where wrslsdate || wrslstime || char(wrslsistr) ||
                        char(wrslsreg#)
                  in (select wrslsdate || wrslstime || char(wrslsistr) ||
                      char(wrslsreg#) from Duplication)
                  order by wrslsdate, wrslstime, wrslsistr, wrslsreg#,
                           wrslstxn#, wrslslin#
                  for update of wrslslin#;
              exec sql open cSales;

              // Start retrieving the data
              dou SQLCOD < 0 or SQLCOD = 100;
                  exec sql fetch cSales into :pDuplicate;
                  if SQLCOD >= 0 and SQLCOD <> 100;
                      exsr srRenumber;
                      exec sql
                          update SALES
                          set WRSLSLIN# = :NewLine
                          where current of cSales;
                  endif;
              enddo;

              // Close the cursor and quit
              exec sql close cSales;

          endsr;

      /end-free
      *------------------------------------------------------------------------*
      * Subroutine: Renumber                                                   *
      *------------------------------------------------------------------------*
      /free

          begsr srRenumber;

              if pDuplicate.Date <> SaveDate or
                 pDuplicate.Time <> SaveTime or
                 pDuplicate.Store <> SaveStore or
                 pDuplicate.Register <> SaveReg or
                 pDuplicate.Transaction <> SaveTxn;

                  SaveDate = pDuplicate.Date;
                  SaveTime = pDuplicate.Time;
                  SaveStore = pDuplicate.Store;
                  SaveReg = pDuplicate.Register;
                  SaveTxn = pDuplicate.Transaction;

                  NewLine = 0;
              endif;

              NewLine += 1;

          endsr;

      /end-free
      *------------------------------------------------------------------------*
      * Subroutine: Reset                                                      *
      *             Initialise the Save* Values                                *
      *------------------------------------------------------------------------*
      /free

          begsr srReset;

              SaveDate = *BLANKS;
              SaveTime = *BLANKS;
              SaveStore = *ZERO;
              SaveReg = *ZERO;
              SaveTxn = *ZERO;

          endsr;

      /end-free
      *------------------------------------------------------------------------*

Flattr this!

Using SQL Fetch/Update in RPG

I’m not going to criticise anyone for trying to move away from native I/O on the IBM i and into SQL, but I have seen a few eye-watering attempts. So in the spirit of sharing knowledge and making life a bit easier for all of us, here is an example of reading and updating a table using SQL.

Let’s start with the table we’re updating. It’s a very simple table and simply maps the SoldTo/ShipTo combination in the distibution system to a Store number in the retail system. You can build a simplified version of this using the following:

create table                                                   
testlib.sqltest
(index integer not null
 GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
 Soldto char (5) not null,
 shipto char (4) not null, 
 store numeric (5, 0) not null)

The requirement is that, based on various business rules, some SoldTo numbers in this table need to be changed. In order to keep things simple, the below program applies a simple rule of: If the store number is less than ten, change the SoldTo to 99999. (I know, I said I was keeping things simple).

The program, as it stands, is pretty much as simple as it gets, but there are couple of points that are worth noting explicitly.

Firstly, when we declare the cursor, we need to include a For Update clause to identify which field (or fields) can be updated.

Secondly, in the Update statement, the Where Current of C1 clause applies the update to all records returned by the cursor. That means that flexible mass updates with a minimum of logical mess are now at your fingertips.

     H
     D SqlRecord       DS
     D  SoldTo                        5A
     D  ShipTo                        4A
     D  Store                         5S 0
     D
     D NewSoldTo       S              5A
      /Free

         Exec Sql
            Declare C1 cursor for
               Select SoldTo, ShipTo, Store
               From SqlTest
               For Update of SoldTo;

         Exec Sql
            Open C1;

         Dou SQLSTATE>='02000';
            Exec Sql
               Fetch C1 into :SqlRecord;

            If SQLSTATE<'02000';
               If Store<=10;
                  NewSoldTo='99999';
                  Exec Sql
                     Update SqlTest
                     Set SoldTo = :NewSoldTo
                     Where current of C1;
               Endif;
            Endif;
         Enddo;

         Exec Sql
            Close C1;

         *INLR=*ON;

      /End-Free                             

Flattr this!

A better way to display ad-hoc messages

When debugging – and when developing quick and dirty utilities – I often resort to the DSPLY opcode. It isn’t pretty, but it puts information onto the screen without having to bother with DDS. There is a better way.

Display Long Text (QUILNGTX) API displays a pop-up window containing the string of text passed to it. You can even add a title to the window by passing a Message ID and (optionally) a qualified message file name.

This API has been around for a while, but it’s new to me and, after playing around with it a bit, I have now updated the DSPOSRLS utility to take advantage of it.

Pretty, isn’t it?

For the sake of completeness, I should probably add a message to a message file for this so that I can display a window title. For the sake of laziness, that’s something for the future.

Flattr this!

Better coding starts with a design

David Shirey’s article, Managing RPG Program Complexity is, as the title suggests, aimed primarily at IBM i developers. The general principles, however, are true for anyone doing any sort of development and is worth a read. I particularly liked the last couple of paragraphs:

Limiting program complexity is a way of thinking, not a series of tips, and it starts with thinking through your design before you begin putting code on the screen. What are you trying to accomplish? What individual logical ideas make up your overall goal? How do you want to structure those ideas using programs and subprocedures?

… Most important, simplicity doesn’t just happen. Complexity just happens—you have to work at simplicity. But in today’s world, I can’t think of a more worthy endeavor.

If you didn’t manage to read that far, the TL;DR version is that any application consists of several descrete logical steps. Each of these discrete steps should be coded seperately and should be small enough that anyone (with the relavant language skills, obiously) should be able to glance at it and know exactly what it is doing. How you break out the various functional pieces is less important than that you do break them out.

Or, to rephrase it slightly, people who write massively monolithic programs deserve to have their fingers broken.

Flattr this!

Debugging RPG Programs in Batch

If you spend any time developing RPG applications you will find that, sooner or later, you will need to debug a program that runs in batch. There are a few steps that you need to take in order to do this and, because my memory is terrible, I’m putting them here.

  • Know what job queue your job is being submitted to. Hold this job queue.
  • Submit your job.
  • Your job is now sitting on the held job queue waiting to be released. Display the job and make a note of the user name, job name and number.
  • Start a service job using STRSRVJOB entering the name, job name and number from the previous step.
  • STRDBG PGM(ProgramName). You can’t enter any breakpoints yet, so hit F12 to exit the source display
  • Release the job queue
  • The Start Serviced Job screen will be displayed asking you to press F10 to enter debug commands for the job. Press F10.
  • DSPMODSRC and enter your breakpoints.
  • Press F12 to resume and F3 to return to the Start Serviced Job screen.
  • Press Enter to start your job.

The job will now begin running and will stop, passing control back to your screen, when the first breakpoint is reached.

Flattr this!