Techniques for working your way out of the System/36 Environment
by Ernie Malaga
This month we begin a new column dedicated to those who have migrated from the System/36 to the AS/400, but are still stuck in the S/36 Execution Environment (S/36E). In "Going Native," we will show you strategies and techniques that will help you accelerate your move out of the S/36E. We welcome any suggestions that will help make this column exactly what you need.
Replacing DELETE and BLDFILE
S/36 software usually has DELETEs and BLDFILEs in pairs in order to eliminate the records contained in a file, or when a file is used temporarily, typically in batch applications. This technique works well on the S/36 because all that system must do to delete a file is erase a VTOC entry. Building a file is equally easy, as the system simply finds a suitable empty spot on the disk, and reserves it for the new file by writing a new VTOC entry.
On the AS/400, on the other hand, deleting a file object does considerably more than erasing an entry somewhere - it actually erases the data, as if you were running S/36 procedure:
DELETE FILENAME,F1,ERASE
Creating a file object is also a complicated process not unlike compiling a program. Because the AS/400 is a database-oriented machine, the creation of a file stores not only the name of the file in the library's directory, but it also stores the names of all the fields, their lengths and types, access path, record format name, and so on. Because of this, file deletes are very slow. For this reason you should avoid using the DELETE and BLDFILE procedures whenever possible as you work in the S/36E. You should also avoid using the equivalent Native commands, Delete File (DLTF) and Create Physical File (CRTPF) when you develop an application in Native. They are just too slow and resource-intensive, degrading system performance.
So, what is the alternative? It is far more efficient to create your "work" files only once, manually, and leave them on the system permanently. Then, as soon as you are done using the records in that file, simply run the Clear Physical File Member (CLRPFM) command to erase the data.
You are probably thinking that it will waste a lot of space to keep those work files on the system all the time. That would be the case if the work files were left full of data records. If the file is left cleared, however, the space consumption will be minimal. With this in mind, let's see how CLRPFM works.
The CLRPFM command has only two parameters: FILE and MBR. The FILE parameter holds the qualified name (library/file) of the file. The MBR (member) parameter holds the name of the member. MBR can actually contain the special values *FIRST (which is the default) or *LAST in addition to an actual name. Unfortunately, it cannot contain the special value *ALL, so you must clear one member at a time. A typical S/36 procedure using temporary work files would look like this:
// BLDFILE WORK,S,R,1000,256,,J // LOAD PGM1 // RUN // LOAD PGM2 // RUN // DELETE WORK,F1
The equivalent in Native would be a CL program looking like this:
CALL PGM(PGM1) CALL PGM(PGM2) CLRPFM FILE(WORK) + MBR(*FIRST)
Using Substitution Parameters in File Names
This topic is related to the one we just discussed. S/36 programmers are fond of creating work files that contain, as part of their names, the Workstation ID that is using them. After all, it is very easy to do in OCL using the following technique:
BLDFILE WORK?WS?,S,R,5000,256,,J
The reasoning behind this technique is that each user should have a different work file for his or her data. Since each workstation can be used by one person only, it is safe to assume that if the file has the workstation ID as part of its name, the file names will be unique. And indeed they are.
This technique works well on the S/36 because workstation IDs have only two characters, while file names can have a maximum of eight. This leaves six characters, enough to make up a meaningful generic name for all the work files.
On the AS/400, however, workstation IDs can have names of up to ten characters, and so can files, meaning that there are not positions left to make up any kind of generic name: the entire width of the file name would have to be used up by the workstation ID. This is clearly an invalid approach.
Fortunately, the AS/400 supports a concept that S/36 programmers don't have available: database files can be divided into members, and each member is treated by HLL programs as if it were the entire file. So, rather than creating separate work files (for each workstation), try the following approach:
1. Create a single work file only once, as discussed in the previous topic. Name the file something that describes the job. The name of the file must not have the name of the workstation who is going to use it. All ten characters, then, can be devoted to a meaningful name.
2. Each time a job needs this work file, add a member to the work file, naming it after the job name (the workstation ID) only. This addition will have to be conditioned in case it has already been added in a previous run of the same program.
DCL VAR(&JOB) + TYPE(*CHAR) + LEN(10) RTVJOBA JOB(&JOB) CHKOBJ OBJ(MYLIB/WORKFILE) + OBJTYPE(*FILE) + MBR(&JOB) MONMSG MSGID(CPF9815) + EXEC(DO) ADDPFM FILE(MYLIB/WORKFILE) + MBR(&JOB) ENDDO
Let's examine what we have done. The DCL statement declares variable &JOB as being a character variable, 10 bytes long. This variable is then used by the Retrieve Job Attributes (RTVJOBA) command to retrieve the name of the job - which, for interactive jobs, matches the workstation ID. Then, the Check Object (CHKOBJ) command is run to find out if member &JOB exists in file WORKFILE from library MYLIB, the names I am assuming for this example. If the member is not found, the system issues message CPF9815, which is captured by the Monitor Message (MONMSG) command; in that case, it executes a DO group consisting of the Add Physical File Member (ADDPFM) command only. ADDPFM adds member &JOB to MYLIB/WORKFILE. Then the job continues.
3. The newly created member will have no records. Now you can use this member as if it were the entire file because, to HLLs, the first record found will be the first record of the member, and the end of file condition will be raised on the last record of the member. To use the new member, do the following:
OVRDBF FILE(WORKFILE) MBR(&JOB) CALL PGM(PGM1) DLTOVR FILE(WORKFILE)
The Override Database File (OVRDBF) command issued above instructs the system to use member &JOB instead of the *FIRST member, which is the default. When program PGM1 is called, the records in member &JOB will be accessed, and no others. Because file overrides remain in effect until the job ends, until the file is overridden again, we need to delete the override (DLTOVR command) issued to that file.
4. At the end of the job, or as soon as the records are no longer needed, you simply clear the member:
CLRPFM FILE(MYLIB/WORKFILE) MBR(&JOB)
This approach effectively offers up to 20 characters for a "work file" name.
Eliminate ALOCLIBR, COMPRESS, CONDENSE, and KEYSORT
These four S/36 procedures do absolutely nothing on the AS/400. The S/36E will simply check them for syntax and go on to the next procedure statement.
Including any of these procedure names within your AS/400 OCL procedures is simply an invitation to disaster - all it takes is a typographical error (such as CONDESNE instead of CONDENSE) and your procedure will abort with a "Procedure not found" error. It is not only safer to avoid them altogether, but it also saves execution time since the OCL interpreter does not have to read the statement only to discover that nothing needs to be done.
The reason these procedures are not needed (and indeed have no equivalent in Native) has to do with the method employed by the system to store information on disk.
ALOCLIBR and CONDENSE are devices of the S/36 operating system to reclaim empty space (or enlarge it) in libraries. Because an AS/400 library is nothing more than a directory of objects, however, the contents of such a library do not have to occupy contiguous space on disk - they do not even have to be on the same disk drive.
The reason COMPRESS is unnecessary is similar. The data contained in all kinds of objects (files, message queues, output queues, and so on) can be anywhere on disk, wherever the system finds an empty spot large enough to store bits and pieces of the information. Obviously, then, the AS/400 does not care how fragmented this empty space is which, in turn, makes the issue of a COMPRESS equivalent quite moot.
Indexed files have a different structure in the AS/400. You will recall that the S/36 has a sequential "index area" where the indexes are accumulated as records arrive. Periodically, the system needs the keys in the index area sorted in ascending sequence in order to improve performance. IPL does this, and KEYSORT does it too. On the AS/400, however, keys are arranged in a "binary tree" structure. A discussion of the binary tree structure is well beyond the scope of this column; suffice it to say that it makes key sorting unnecessary.
Reorganizing Data Files
When a file record is deleted, the record actually remains in the file in an unreadable format. The record is not physically removed from the file. For this reason, deleted records tend to accumulate in AS/400 database files just like they do in S/36 files.
File reorganization is the process whereby deleted records are physically removed from the file. This process must be carried out periodically to avoid wasting space on the disk.
The S/36 reorganizes files with a sequence of procedure calls. Let's assume that we have to reorganize file CUSTOMER:
COPYDATA CUSTOMER,,TEMPFILE, ,,,,REORG DELETE CUSTOMER,F1 RENAME TEMPFILE,CUSTOMER
But wait. What if file CUSTOMER has alternate indexes? "Well," you might say, "we would have to DELETE all alternate index files before the reorganize, and re-build them afterwards." And the S/36 has no command or procedure to help you find those alternate indexes, except by a tedious examination of a disk catalog. You probably end up hard-coding a DELETE procedure call for each alternate index file before the reorganize, and a BLDINDEX procedure call to re-build each alternate index all over again.
Fortunately the Native AS/400 Reorganize Physical File Member command (RGZPFM) is much more efficient in this respect. The RGZPFM command takes care of all reorganizing tasks, and all logical files (the equivalent of alternate index files) can stay on the system while the reorganization is in progress. Pretty slick, actually.
The moral of this story is that you should never "translate" line-by-line your S/36 code into Native code. Doing so would have resulted in a tremendously inefficient program.
ALLOCATE and DEALLOC
The two OCL statements allocate and dealloc are used in many S/36 procedures to reserve exclusive use of the diskette or tape drive. This can be translated into Native as the Allocate Object (ALCOBJ) and Deallocate Object (DLCOBJ) commands, with the added benefit that the Native commands can be used for any type of object - not only diskette or tape drives, but even database files, user profiles, message queues, and so on.
For example, ALCOBJ OBJ(DKT01 *DEVD *EXCL) allocates DKT01, the device description for the diskette drive, for exclusive use by this job. DLCOBJ OBJ(DKT01 *DEVD *EXCL) deallocates the same object from exclusive use.
File Names and Labels
Yet another S/36 technique is the use of logical and physical names for data files: a logical name is used by the program while a different physical name is actually given to the file on disk. For example, the file may be called SOURCE by an RPG program, and yet the disk knows this file as GLTRANS. The S/36 solves this problem by using the following code:
// FILE NAME-SOURCE, LABEL-GLTRANS
In this way, the program can refer to file GLTRANS by the name SOURCE. In other words, if the program were written in RPG, the F-specs would have SOURCE as the file name.
The same thing can be done in Native with the Override Database File (OVRDBF) and Delete Override (DLTOVR) commands:
OVRDBF FILE(SOURCE) + TOFILE(GLTRANS) CALL PGM(...) DLTOVR FILE(SOURCE)
The DLTOVR command is needed because the name override performed by OVRDBF stays in effect until the job ends, until overridden again, or until the DLTOVR command is executed on the same file.
The Local Data Area (LDA)
The LDA is available on both systems. Unlike the S/36 LDA, the AS/400 LDA is 1024 positions long.
On the S/36, the LDA is used extensively to pass information between programs and between procedures and programs. It is the simplest way to pass data between programs. On the AS/400, however, you should consider using parameters as your primary avenue for passing data between programs.
For example, on the S/36 you may have written the following code:
// LOCAL OFFSET-21,DATA-'ABC' // LOAD PGM1 // RUN
It is true that this can be coded on the AS/400 with equal ease:
CHGDTAARA DTAARA(*LDA (21 3)) + VALUE('ABC') CALL PGM(PGM1)
However, passing 'ABC' as a parameter is far more convenient:
CALL PGM(PGM1) PARM('ABC')
When the program starts running, it will receive the parameter from the calling program. Different languages require different coding to accomplish this task. If the called program were a CL program, it would have to be coded like this:
PGM1: PGM PARM(&PARM1) DCL VAR(&PARM1) + TYPE(*CHAR) + LEN(3)
If the called program is an RPG program, on the other hand, the C-specs will have to contain the following:
*ENTRY PLIST PARM PARM1 3
Notice that in both cases the "receiving" parameter is named PARM1 for simplicity, and is defined as a three-byte alphanumeric field.
The // PRINTER statement
Most of us have used the // PRINTER statement extensively in order to control printer settings such as number of copies, forms, printer ID, and others. The AS/400 has an equivalent in the Override Printer File command (OVRPRTF), followed a program call, followed by a Delete Override command (DLTOVR) to cancel the overrides previously made.
There is, however, a better method. Keep in mind that the AS/400 is more database-oriented. Files are permanent objects, including printer files. If you define your printer files externally by writing the DDS and compiling them with the Create Printer File command (CRTPRTF), the printer file will exist on the system until the end of time or until you delete it, whichever comes first.
Because all the "settings" for the printer are already included in the printer file object, there will be no need for you to keep overriding the forms number or printer ID each time you use this printer file. In other words, you can relax and let the system take care of the printing for you.
For instance, you may create a printer file for your Payroll Checks. Because these forms are so "sensitive," it is clear that they should not print on just any printer. Also, the forms number should always be set to, say, CHEK. Because these forms are 24 lines long only, the S/36 programmer must code the following on each procedure that prints payroll checks:
// PRINTER NAME-PRCHECKS,+ DEVICE-PR,+ LINES-24,+ FORMSNO-CHEK,+ ALIGN-YES
On the AS/400 you could write the DDS for the printer file and create the file with the following command:
CRTPRTF FILE(PRCHECKS) + SRCFILE(MYLIB/QDDSSRC) + PAGESIZE(24 80) + OVRFLW(23) + RPLUNPRT(*YES ' ') + ALIGN(*YES) + OUTQ(PR_PRINTER) + FORMTYPE(PR_CHECKS)
Because all the "settings" are contained in the printer file object itself, it will always "know" them. As a bonus, the printer file "knows" its overflow line number and the fact that unprintable characters should be replaced by blanks - you will not have to code these overrides in your RPG program L-specs
or H-spec.
Substituting for ?DATE? and ?TIME?
The Retrieve System Value command (RTVSYSVAL) lets you retrieve the contents of any of the many system values. Two of these system values, QDATE and QTIME, can be used to replace the ?DATE? and ?TIME? substitution expressions in OCL.
RTVSYSVAL SYSVAL(QDATE) VALUE (&SYSDATE), for example, will get the current system date and place it in variable &SYSDATE. Like all CL program variables, it must be previously declared with the DCL command. Since the system date is a six-byte character field, it is only proper to declare &SYSDATE as TYPE(*CHAR) LEN(6).
In a similar fashion, RTVSYSVAL SYSVAL(QTIME) VALUE (&SYSTIME) can be used to retrieve the system time. However, the AS/400 goes beyond the S/36 in that you can retrieve portions of the system date and time individually. There are separate system values to hold the month, day, and year (QMONTH, QDAY, QYEAR), as well as the hour, minutes, and seconds (QHOUR, QMINUTE, QSECOND).
LATEST COMMENTS
MC Press Online