Use it or... well, use it.
Over the years, I've seen a number of people using the READE (read equal key) opcode in their RPG III and RPG IV programs. But I've also seen a handful of people not using it in situations where it was meant to be used.
Let's look at the READE opcode and its sibling READPE in more detail to make sure we're all using it to our advantage.
READE (Read Equal Key) allows you to read a database record in keyed sequence. If the next key matches that of the keylist specified, the record is returned to the program. If it doesn't match, you get the lovely %EOF (end of file) condition.
READPE (Read Prior Equal Key) does the same thing, but in reverse, reading upward toward the beginning of the file. Like READE, when READPE detects a key different from the keylist, it too signals an end-of-file condition. The %EOF built-in function is also used in this situation.
Remember, when %EOF is signaled the file must be repositioned before reading it again with a READE, READPE, READ, or READP. The CHAIN, SETLL, or SETGT are typically used to reposition the file after an end-of-file condition.
Typically, READE is used with a SETLL or CHAIN opcode to position the file and then read the set of records with a key equal to that of the keylist. Often I see people using SETLL or CHAIN and then use a READ opcode followed by an IF statement to check the record just read to see if its key fields match. Something like this:
/free
chain (actnbr) custmast;
if %found();
dow not %EOF();
if (actnbr = c.acctno);
// do whatever
endif;
read custmast;
enddo;
endif;
/end-free
In this example, a CHAIN opcode retrieves the first record that we're looking for, and then using a combination of %FOUND and a DOU (do until) loop reads through the file until the key changes or end of file is detect. I think, officially, this is called the Yuk! style of coding.
Using READE, this type of loop would be written something like the following:
/free
setll (actnbr) custmast;
if %EQUAL();
reade (actnbr) custmast;
dow not %EOF();
// do whatever
reade (actnbr) custmast;
enddo;
endif;
/end-free
The inherent problem with the DOW versus DOU looping and duplicating the conditional statement or duplicating the READE operation isn't the focus of this article, so I won't go into that.
By using a READE operation, you delegate the responsibility of comparing the key fields just read into the program from the file to the opcode.
These examples are in free format but also apply to traditional RPG IV syntax. However, there is a feature of the free-format syntax that is not available in traditional syntax, and that's the ad hoc keylist (which I've been using here).
Ad hoc keylists are an alternative to the traditional KLIST/KFLD opcodes. By wrapping the fields used in the keylist in parentheses, you're instructing the compiler to generate an ad hoc keylist. For example:
C ORDLINE KLIST
C KFLD ORDNBR
C KFLD LINENO
C ORDER KLIST
C KFLD ORDNBR
C eval lineno = 1
C eval ordbr = 3741
C ORDLINE CHAIN ORDERFILE
C if %found()
C dow NOT %EOF()
C eval total += qtyord*price
C ORDER reade orderfile
C enddo
C endif
In this example, the ORDLINE keylist is used to position the file to the first record that matches the data in the fields in the keylist. Then the READE opcode is use with another keylist, ORDER, to continue to retrieve each record in the file with a key that matches the fields in this keylist.
In free format, this code would be written as follows:
/free
chain (ordnbr:lineno) orderfile;
if %found();
dow not %EOF();
total += qtyord*pric;
reade (ordnbr) orderfile;
enddo;
endif;
/end-free
Note the use of the (ordnbr:lineno) and (ordnbr) parameters of the CHAIN and the READE opcodes. This is how ad hoc keylists are specified. No read KLIST/KFLD opcode required. The READE (and CHAIN) opcodes assemble an ad hoc keylist for you by using the fields in the parentheses.
Using an ad hoc keylist, you can avoid declaring keylists in the Calc specs (which doesn't feel right when using /FREE syntax anyway).
| RPG TnT: 101 Dynamic Tips 'n Techniques with RPG IV by Bob Cozzi is available now. | |
![]() |
The latest book from author Bob Cozzi is available now. This 300-page book contains 101 example RPG IV Tips and Techniques for everyday programming tasks, from date calculations, to regular express searches, to using APIs. Cozzi wrote down every cool technique he's found over the years, updated them, and consolidated them into this compact book that is a great desktop companion. And it includes full example source code. Order your copy today. |
There's more to ad hoc keylists; you can also use a data structure as the keylist. If you have a data structure that contains the key fields for your READE or CHAIN access, you can use the data structure name as an ad hoc keylist. But you can't enclose it in paren; instead, you wrap it in gthe %KDS keyword, as follows:
reade %kds(ordStruct) orderfile;
This uses the ORDSTRUCT data structure's subfield as an ad hoc keylist. If you have more fields in the data structure than are used for the keylist, then you specify a second parameter on the %KDS keyword to indicate how many subfields should be used for the keylist, as follows:
chain %kds(ordStruct:2) orderfile;
reade %kds(ordStruct:1) orderfile;
This causes the first two and first fields, respectively to be used as the ad hoc keylist.
So don't assume READE isn't right for you. More often than not, reading a record using the READ opcode isn't strictly applicable in many situations. Give it a try if you haven't to see what you've been missing.
Bob Cozzi, a black belt in Judo, started practicing Judo in 1974 and began to learn programming in 1975. Today, Bob runs RPG World, the most popular third-party, commercial RPG IV Training Event in the world. The next RPG World Conference is open to all, and is coming up in May 2008.
-----------------------------------
READE in RPG has this problem with temporarily locking records it doesn't actually read and it has annoyed a select set of programmers in the AS/400 community for years (going all the way back to the System/38). This latest PMR (ppppp,bbb,ccc) was the last straw; something has to be done. It's not that this problem is so monumental, it's that the very idea is just so counter-intuitive and the steps we have to take to circumvent it are just so
Here's our problem: A program has two logical files open to the same physical. In file "1" record "A" is locked. File "2" is read using SETLL and then a READE loop using a key that will read records in a separate block from record "A". The problem is that record "A" is right next to this block of records so at the end of the block the program fails because it is trying to get a temporary lock on record "A" to see what's its key value is before deciding whether or not it will signal an end-of-file. To get around this problem the program has to do READE(N) and then a CHAIN to lock the record inside the loop, so we're forced to waste I/O cycles doing double-reads.
Do you see the illogic in what it's doing? If we're reading with READE(N) on a file open for update then it's not locking the record even temporarily to check the key value, so why does it do so with a regular READE. In RPG I can do a SETLL on a file open for update and it will put the key values in the File Information Data Structure without locking the record. Now if I can do that with SETLL (getting key values without locking the record) then why can't READE do that too?
Here's another problem: Jobs using commitment control and holding onto all record locks end up having other jobs failing trying to lock records because READE got temporary locks on one or more records it ultimately didn't read and the system won't release the locks because of the commitment control settings.
All this would be resolved if READE didn't do temporary locks to check key values.
From a post to Midrange-L by Barbara Morris:
http://archive.midrange.com/rpg400-l/200503/msg00899.html
"Right, I wouldn't, not that I'm an expert in database performance. While READE does prevent blocking, it does do the key comparison correctly (*). Using READ and checking the key yourself from the INFDS can get you into a mess if you have any special key features like ALTSEQ, ABSVAL, ALWNULL (see the READE documentation for the list of things that can cause a problem if you do a character-by-character key comparison). You can get mysterious results if you use a SETLL (which always sets %FOUND correctly) followed by a READ+compare (which might cause even the first record not to match).
Even if you don't have special key features, and doing your own key compare would always be valid, you have to weigh the blocking benefit against the added complexity of your code getting the key value out of the INFDS, plus the hidden requirement that your keys never have any special features added. And remember that your code might be used as a pattern in some situation where the file does have special key features.
(*) Only the ILE RPG (RPG IV) compiler does the READE key comparison (and SETLL EQ comparison) correctly. The RPG/400 (RPG III) compiler does a byte-wise comparison from the key in the feedback area, so using a READE in RPG/400 wouldn't have any _correctness_ benefit over your own READ + compare."
Charles