Reading Database Files in CL with DCLF and RCVF

CL can read a database file directly, without calling an RPG or SQL program, using two commands: DCLF to declare the file and generate a variable for every field, and RCVF to read one record at a time. This page builds a small read loop and explains the CL equivalent of checking %EOF. For the surrounding program skeleton, see CL Program Structure: PGM, DCL, and ENDPGM; for the variables DCLF creates, see CL Variables, Data Types, and CHGVAR.

Declaring the File with DCLF

DCLF FILE(library-name/file-name) RCDFMT(record-format-names) OPNID(open-id)

Verification level: IBM DOC CHECKED, against the IBM i 7.5 DCLF command reference.

ParameterPurposeDefault
FILEThe database or display file to declare (required, positional 1)None; library defaults to *LIBL
RCDFMTWhich record formats to declare variables for*ALL
OPNIDOpen file identifier, used to make multiple declarations of the same file unique*NONE
ALWNULLWhether null field values are allowed*NO
ALWVARLENWhether variable-length fields are allowed*NO

For a simple read, naming just the file is enough:

PGM
  DCLF FILE(MYLIB/CUSTOMER)
  ...
ENDPGM

Verification level: IBM DOC CHECKED.

The compiler generates one CL variable per field in the record format, named &fieldname (or &opnid_fieldname when OPNID is used, which matters once a program declares the same file twice or needs to distinguish fields from two files that happen to share names). DCLF is a declare command like DCL — it must come right after PGM, before any executable command, and a program can have at most five DCLF commands. Only physical and logical database files with a single usable record format, or display files, can be declared this way; files with multiple formats need RCDFMT to pick specific ones, up to 50 named formats or *ALL for up to 99.

A database file declared with DCLF is opened for input only, and only RCVF is valid against it — SNDF, SNDRCVF, and WAIT are display-file-only operations and are rejected for a database file.

Reading Records with RCVF

RCVF [DEV(*FILE | name)] [RCDFMT(*FILE | name)] [OPNID(*NONE | name)] [WAIT(*YES | *NO)]

Verification level: IBM DOC CHECKED, against the IBM i 7.5 RCVF command reference.

For a database file, the plain form is all that’s needed:

RCVF

The WAIT and DEV parameters apply only to display device files; the IBM i 7.5 reference states this restriction explicitly for both parameters, and specifying either against a database file is rejected. A database file is implicitly opened on the first RCVF against it and stays open for input until the program ends or the end-of-file condition described below is reached.

Each RCVF populates the field variables that DCLF declared with the next record’s values. There is no separate “read” statement — RCVF is both the open-on-first-use and the per-record read.

End of File: MONMSG CPF0864

CL has no %EOF built-in function. Instead, when RCVF runs past the last record, the file sends the program an escape message, CPF0864 (“End of file detected for file &1 in &2”), and the standard pattern is a label, a read, a MONMSG that branches out of the loop, and the record-processing logic:

PGM
  DCLF FILE(MYLIB/CUSTOMER)
  DCL VAR(&COUNT) TYPE(*DEC) LEN(5 0) VALUE(0)

READLOOP:
  RCVF
  MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(DONE))

  CHGVAR VAR(&COUNT) VALUE(&COUNT + 1)
  /* process &CUSTNO, &CUSTNM, and other declared fields here */

  GOTO CMDLBL(READLOOP)

DONE:
  SNDPGMMSG MSG('Read' *CAT %CHAR(&COUNT) *CAT 'customer records.') TOPGMQ(*EXT)
ENDPGM

Verification level: IBM DOC CHECKED. This loop structure matches the pattern shown in the IBM RCVF reference’s own end-of-file example; it has not been compiled or run.

Mechanically this is CL’s version of a DOU %EOF() loop in RPGLE: the MONMSG right after RCVF is the equivalent of checking the end-of-file indicator before processing the record, except CL expresses it as a message monitor and a GOTO rather than a boolean test.

Two rules make this pattern easy to get wrong:

  • If CPF0864 is not monitored, the program does not simply stop — it goes into an inquiry message wait (MSGW) once the unhandled escape message reaches the top of the call stack with no monitor, which on an interactive job means the session hangs waiting for an operator reply. See IBM i Troubleshooting Guide if a job is already stuck this way.
  • Once a given RCVF against a file has returned CPF0864 within the current program activation, further RCVF calls against that same open instance keep returning CPF0864. The file does not automatically reposition to the start. Reading the file again within the same program run typically means closing and reopening it — with the CLOSE command (CLOSE OPNID(opnid), or plain CLOSE for the default open identifier), not CLOF. CLOSE is documented specifically for a database file that RCVF implicitly opened against a DCLF declaration in the same program or ILE CL procedure; CLOF is a different command for files opened with OPNDBF or OPNQRYF, which is not how a DCLF/RCVF file is opened. The next RCVF after CLOSE reopens the file and reads from the start. An alternative is to drive the read from a separately called program instead of closing and reopening in place.

Putting It Together

A complete small program that totals an amount field while reading a physical file:

PGM
  DCLF FILE(MYLIB/ORDERS)
  DCL VAR(&TOTAL) TYPE(*DEC) LEN(11 2) VALUE(0)

READLOOP:
  RCVF
  MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(DONE))

  CHGVAR VAR(&TOTAL) VALUE(&TOTAL + &ORDAMT)
  GOTO CMDLBL(READLOOP)

DONE:
  SNDPGMMSG MSG('Order total:' *CAT %CHAR(&TOTAL)) TOPGMQ(*EXT)
ENDPGM

Verification level: IBM DOC CHECKED. Not compiled or run; &ORDAMT is illustrative and assumes a field of that name exists in the ORDERS record format.

Compile it the same way as any other CL program, per CL Program Structure: PGM, DCL, and ENDPGM:

CRTBNDCL PGM(MYLIB/ORDTOTAL) SRCFILE(MYLIB/QCLSRC) SRCMBR(ORDTOTAL)

Common Mistakes

  • Forgetting the MONMSG CPF0864 after RCVF. The job ends up waiting in MSGW instead of exiting the loop cleanly.
  • Trying to RCVF a database file with WAIT or DEV set. Those parameters are display-file-only and the command is rejected.
  • Declaring more than five files with DCLF in one program. The limit is five; split the work across multiple programs if more are genuinely needed.
  • Expecting a second pass over the file within the same program run without reopening it. Once CPF0864 has been returned for that open instance, it keeps being returned.

Summary

  • DCLF FILE(lib/file) declares a database file and generates one variable per field, up to five files per program.
  • RCVF reads the next record; for database files, omit WAIT and DEV, which are display-file-only.
  • End of file is the escape message CPF0864, handled with MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(label)) right after RCVF — CL’s structural equivalent of checking %EOF.
  • Once a file hits end of file in a program’s open instance, that instance cannot RCVF it again without reopening.

Sources: IBM i 7.5 DCLF command, IBM i 7.5 RCVF command, and IBM i 7.5 CLOSE command. All examples are documentation-checked only; none were compiled or run.

For error handling beyond a single MONMSG, see IBM i Message Files, Exception Handling and CL. For what to do when a job is actually stuck waiting on a message, see IBM i Troubleshooting Guide.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top