Special Files Can Do It All
April 13, 2005 Ted Holt
A special file is a file that is a front-end for a program. That is, when an I/O request occurs to the special file, the system calls a program. The ILE RPG Programmer’s Guide refers to this called program as the “user-written routine.” Special files provide a way for RPG to do anything that a program can do.
To tell an RPG program that a file is special file, use the device type SPECIAL in the file description specifications. In the keyword section of the F spec, use the PGMNAME keyword to name the user-written routine that is to be called. In the following example, file MYFILE is a special file for user-written routine MYPGM.
FMYFILE IP E SPECIAL PGMNAME('MYPGM')
The system automatically passes four parameters, which I describe in the following table, to the user-written routine.
Parameter |
Type |
Length |
Description |
Option |
Character |
1 |
The action the user-written O: open the file C: close the file F: force end of file R: read a record W: write a record D: delete a record U: update the last record |
Status |
Character |
1 |
The result of the 0 (zero): normal 1: end of file 2: error |
Error |
Packed |
5,0 |
A user-defined value to |
Input-output area |
Character |
Varies |
The record buffer for the |
Do not define these parameters in the program that uses the special file. However, you must define them in the user-written routine. We’ll get to that in just a bit.
When an I/O operation is issued to the special file, the system calls the user-written routine. In the Option parameter, the system indicates the operation that the system wishes to have performed. The user-written routine does whatever needs to be done to carry out the request, and depending on the success or failure of the operation, loads one or more of the second through fourth parameters.
You may pass additional parameters to the user-written routine. In the F spec for the special file, name a PLIST that contains the additional parameters. In the user-routine routine, add the additional parameters to the end of the entry parameter list. This means that the parameter list in the calling program will have four fewer parameters than the entry parameter list in the user-written routine.
You must decide what happens in the user-written routine and code the necessary operations to carry out the I/O requests.
For more information about special files, see the ILE RPG Programmer’s Guide (SC09-2507-04)
SQL and the RPG Cycle
The system on which I learned RPG, the System/3 Model 12, required me to use the RPG cycle, and until I began to work with the System/34, the only way I knew to write RPG was with the cycle. These days I rarely use the cycle, but it’s not always by choice. You see, I like the cycle. I like the fact that it does so much work for me, especially when dealing with control breaks. The less code I have to write, the less room there is for me to mess up.
But use of the RPG cycle is banned or discouraged in many shops, and many RPG programmers don’t know the cycle. Those are good reasons to avoid the cycle, but the main reason I avoid it is that it doesn’t work with embedded SQL. However, it occurred to me recently that a special file might let me use the RPG cycle and embedded SQL. I tried it and, to my delight, found that it works. Now I can replace a maxed-out OPNQRYF command with a more powerful SQL command.
Suppose you have some ancient task that was designed with cycle RPG and OPNQRYF. Here’s the RPG program:
FQCUSTCDT IP E K DISK FQSYSPRT O F 132 PRINTER OFLIND(*INOF) ICUSREC 01 I STATE L1 C L1 MOVE *ZERO COUNT1 C C ADD 1 COUNT1 5 0 CL1 ADD COUNT1 COUNTR 5 0 OQSYSPRT H 1P 2006 O 'CUSTOMER LIST' O D 01 2 O LSTNAM +0001 O INIT +0001 O CUSNUM 4 +0001 O CITY +0001 O STATE +0001 O ZIPCOD +0001 O T L1 2 O 'STATE COUNT:' O COUNT1 Z +0001 O T LR 2 O 'TOTAL COUNT:' O COUNTR Z +0001 O +0003 '** END OF REPORT **'
Input comes from a customer file, QCUSTCDT, which you will find in library QIWS on your system. The program uses the cycle to check for a control break. Notice that the L1 indicator conditions detail calculations, total calculations, and total output.
Here’s the CL program. It runs OPNQRYF to select records of customers who owe money, sorted by the state in which they reside, the customer’s last name, and customer’s initials.
PGM OVRDBF FILE(QCUSTCDT) SHARE(*YES) OPNQRYF FILE((QCUSTCDT)) QRYSLT('BALDUE *GT 0') + KEYFLD((STATE) (LSTNAM) (INIT)) CALL PGM(SPECIAL1R) CLOF OPNID(QCUSTCDT) DLTOVR FILE(QCUSTCDT) ENDPGM
Suppose further that you need to change this application in a way that OPNQRYF doesn’t support. Maybe you need a UNION. Maybe you need a left outer join and an inner join within the same query. Wouldn’t it be great if you could use embedded SQL instead of OPNQRYF? If you use a special file, you can replace OPNQRYF with SQL.
The first thing to do is to change the RPG program to define file QCUSTCDT as a special file.
FQCUSTCDT IP E SPECIAL PGMNAME('SPECIAL3R') FQSYSPRT O F 132 PRINTER OFLIND(*INOF) ICUSREC 01 I STATE L1 C L1 MOVE *ZERO COUNT1 C C ADD 1 COUNT1 5 0 CL1 ADD COUNT1 COUNTR 5 0 OQSYSPRT H 1P 2006 O 'CUSTOMER LIST' O D 01 2 O LSTNAM +0001 O INIT +0001 O CUSNUM 4 +0001 O CITY +0001 O STATE +0001 O ZIPCOD +0001 O T L1 2 O 'STATE COUNT:' O COUNT1 Z +0001 O T LR 2 O 'TOTAL COUNT:' O COUNTR Z +0001 O +0003 '** END OF REPORT **'
The second task is to write the user-written routine, program SPECIAL3R.
H option(*srcstmt: *nodebugio) dftactgrp(*no) actgrp(*caller) D Buffer e ds extname(QCUSTCDT) C *ENTRY PLIST C PARM OPTION 1 C PARM STATUS 1 C PARM ERROR 5 0 C PARM BUFFER C C/exec sql C+ declare input cursor for C+ select * from qcustcdt C+ where baldue > 0 C+ order by state, lstnam, init C/end-exec C select C when option = 'O' C reset Buffer C/exec sql C+ open input C/end-exec C when option = 'C' C/exec sql C+ close input C/end-exec C eval *inlr = *on C when option = 'R' C/exec sql C+ fetch input into :Buffer C/end-exec C endsl C C select C when sqlstt < '02000' C eval Status = '0' C when sqlstt = '02000' C eval Status = '1' C other C eval Status = '2' C endsl C move sqlstt error C return
Notice a few things:
- The entry parameter list defines the four parameters I mentioned earlier.
- The last parameter is the record layout of the QCUSTCDT file. I used an externally described data structure to make sure it matches the record format used in the calling program.
- This program handles options O (open the file), C (close the file), and R (read a record) by executing appropriate SQL commands.
- If something goes wrong or end-of-file is reached, I update the Status parameter. I chose to load the SQLSTT (SQL state) variable into the Error parameter.
The result of this exercise is that my RPG report program uses SQL to read from the QCUSTCDT file.
Let’s take this concept one step further. Suppose I want to pass a parameter to the user-written routine to make it select customers from a certain state. First I must change the report program by adding a parameter list to the special file. I cleverly named the parameter list “SPECIAL”. Second, I defined the SPECIAL parameter list as having one parameter–STATE.
FQCUSTCDT IP E SPECIAL PGMNAME('SPECIAL3R') F PLIST(SPECIAL) FQSYSPRT O F 132 PRINTER OFlIND(*INOF) C *ENTRY PLIST C PARM STATE 2 C SPECIAL PLIST C PARM STATE 2 C ADD 1 COUNT 5 0 O* o specs omitted; see previous example
I also have to modify the user-written routine SPECIAL3 to include the extra parameter and refer to it in the SQL SELECT statement.
H option(*srcstmt: *nodebugio) dftactgrp(*no) actgrp(*caller) D Buffer e ds extname(QCUSTCDT) C *ENTRY PLIST C PARM OPTION 1 C PARM STATUS 1 C PARM ERROR 5 0 C PARM BUFFER C PARM PARMSTATE 2 C C/exec sql C+ declare input cursor for C+ select * from qcustcdt C+ where state = :ParmState C+ order by state, lstnam, init C/end-exec **** remainder of program omitted; see previous example
And there you have it: a program that uses the RPG cycle and embedded SQL, thanks to the magic of special files.
Use Your Imagination
Since a special file calls a program, RPG’s I/O ability is limited to what programs can do. The number of ways you can use special files is limited by your imagination.
Click here to contact Ted Holt by e-mail.