Open PL/I's indexed I/O, also called VSAM (Virtual Storage Access Method), involves the use of character-string keys that are stored in the records of the file. Every record has a key, and the keys are ordered lexicographically in an index that is associated with the file. The records can be accessed randomly by specifying the key of the desired record, or they can be accessed sequentially according to the order of the keys, or they can be accessed by a combination of these methods, as illustrated in the following table:.
An INDEXED (equivalently, VSAM) file is declared by including either the INDEXED or the VSAM option of the ENVIRONMENT attribute. For example,
DECLARE MASTER RECORD OUTPUT FILE ENVIRONMENT(VSAM KEYLOC(1) KEYLENGTH(12) RECSIZE(130));
The KEYLOC option specifies the byte position of the first byte of the key in the records, counting the first byte as KEYLOC(1). The KEYLENGTH option specifies the number of characters in the key. RECSIZE specifies the record length or, in the case of a file of variable-length records, it specifies the maximum record length.
Actually, it is necessary to specify RECSIZE, KEYLOC, and KEYLENGTH for a file only when it is created. Programs that read or update an existing indexed file do not need to include these options, although they may. Open PL/I can determine these characteristics from an existing file. If you are using full IBM mode, file header information is used when opening a VSAM file for output, rather than requiring you to specify RECSIZE, KEYLOC, or KEYLENGTH with the ENVIRONMENT attribute.
There are a number of options that can be specified with the ENVIRONMENT attribute. These options are described in the table below. This table also lists a number of options that are not needed or not supported in Open PL/I, but which a user may expect to find based on experience with other compilers.
A random access read of a record in an indexed (VSAM) file opened with the INPUT or UPDATE attribute is accomplished by a READ statement that includes the KEY option. For example,
READ FILE(PARTS) INTO(PART_REC) KEY(PART_NUM);
A sequential read reads the next record of the file after the last one read — using the key ordering — or the first record of the file, if no record has previously been read. For a sequential READ statement, the KEY option is omitted.
If a record has previously been read from a file opened with the UPDATE attribute, and no other intervening I/O operation has been performed, a REWRITE without key may be executed. In this case, the previously read record is overwritten with the new value. For example,
READ FILE(PARTS) INTO(PART_REC) KEY(PART_NUM); PART_REC = NEW VALUE; REWRITE FILE(PARTS) FROM(PART_REC);
However, a record of an indexed file can be randomly updated by specifying the KEYFROM option in the REWRITE statement. For example,
REWRITE FILE(PARTS) FROM(PART_REC) KEYFROM('12XJ04');
If an indexed file has been opened with the OUTPUT or UPDATE attribute, a WRITE statement using the KEYFROM option can be used to add new records to the file. The WRITE statement without KEYFROM is used to sequentially add new records to a file.
The DELETE statement can be used to remove records from an indexed file opened with the UPDATE attribute. The KEY option must be used to delete a record.
All keys used in an indexed (VSAM) file must by of type CHARACTER. If a KEY or KEYFROM option references a value that is not of type CHARACTER, the value is converted to CHARACTER before the I/O operation begins. Note that keys of type PICTURE are converted to CHARACTER without any actual change in the key contents. (See the chapter Data Type Conversions.)
An indexed (VSAM) file can have either fixed-length or variable-length records. The records are fixed-length if the F option is used with the ENVIRONMENT attribute when the file is created; they are variable-length if the V option is used. (See the table above.) If neither F nor V is specified, F is assumed.
In a fixed-length record file, all records must have the same length, which is the length specified by the RECSIZE option of the ENVIRONMENT attribute when the file was created. For a variable-length record file, RECSIZE specifies the maximum record length that is permitted in the file. The MINRECSIZE option must also be specified for a variable-length record file. This value specifies the minimum length that all records in the file must have. The keys of the file must be contained within this minimum portion of the file.
The SCALARVARYING option specifies that there is a two-byte prefix on all records in the data file. A user must take this into account when specifying the KEYLOC value. The KEYLOC value is the absolute byte position of the record starting at 1. This option can be used on either F or V record formats. It allows for more general use of character varying records in I/O statements, although character records can also be transmitted. When a character varying item is used in a WRITE or REWRITE statement, the two-byte prefix is retained with the record as it gets written out to the data file, if the SCALARVARYING option is set. Without the SCALARVARYING option, the prefix is not written; in this case, the remainder of the record is undefined beyond its current length. When a character record is written, a prefix will be added if SCALARVARYING option is set. The SCALARVARYING option lets you write and read records smaller then your max record size, without getting the RECORD condition. It also is useful when doing locate mode I/O.
When setting KEYLOC, it is very important to "compensate" for the two-byte prefix when the SCALARVARYING option is used.
The use of V type records provide a way to transmit partial records. Open PL/I does not require control bytes in the record for this support. The run-time system will automatically retain information concerning the number of bytes transmitted. Since no control information is needed in the record, the user should not change KEYLOC based on the record type. Only the SCALARVARYING option has impact on the KEYLOC location.
Although the PL/I language does not provide a mechanism by which multiple keys can be specified for a file, Open PL/I does provide a means for accessing data via multiple keys. After an indexed file has been created using one key (which is called the primary key), you can use the utility Ipivsam to add additional indexes (that is, keys) to the file. In the PL/I program each different key version of the data must be declared as a separate file. However, all of these declared files can actually be referencing the same set of data. For example,
DECLARE EMPFILE1 RECORD UPDATE FILE ENV(VSAM KEYLOC(1) KEYLENGTH(6) RECSIZE(145)); DECLARE EMPFILE2 RECORD UPDATE FILE ENV(VSAM KEYLOC(31) KEYLENGTH(3) RECSIZE(145) GENKEY); DECLARE 1 EMP_ 2 EMP_ID (CHAR(6), 2 NAME (CHAR(24), 2 DEPARTMT (CHAR(3), ... OPEN FILE(CUSTFILE1) TITLE('employee'); OPEN FILE(CUSTFILE2) TITLE('employee'); /* same file, different key */
The following example program illustrates the use of the ENVIRONMENT variable and its options for I/O on a file with VSAM organization.
/* Example of VSAM I/O */ TEST: PROCEDURE OPTIONS(MAIN); %REPLACE TRUE BY '1'B; %REPLACE FALSE BY '0'B; DECLARE DB FILE ENV(VSAM KEYLOC(21) KEYLENGTH(120) RECSIZE(244)); DECLARE DBG FILE ENV(INDEXED GENKEY KEYLOC(21) KEYLENGTH(120) RECSIZE(244)); DECLARE 1 DB_RECORD, 2 HEADER CHAR(20), 2 DBKEY CHAR(120), 2 STUFF CHAR(100), 2 NUMBER FIXED BIN(31); DECLARE I FIXED BIN(31); DECLARE CS CHAR(26) STATIC INIT('THEQUICKBROWNFXJMPSVLAZYDG'); DECLARE C(26) CHAR DEFINED(CS); DECLARE LETTERS CHAR(30); /* CREATE DB */ OPEN FILE(DB) RECORD KEYED SEQUENTIAL OUTPUT TITLE('MYFILE1'); HEADER = 'DATA RECORD HEADER'; STUFF = COPY('ABCDE',20); DO I = 1 TO 26; NUMBER = I; DBKEY = '@'||C(I) ||COPY('@',118); WRITE FILE(DB) FROM(DB_RECORD) KEYFROM(DBKEY); END; CLOSE FILE(DB); /* TRY A GENERIC READ THEN MOVE SEQUENTIALLY THRU THE FILE */ OPEN FILE(DBG) RECORD KEYED SEQUENTIAL INPUT TITLE('MYFILE1'); ON ENDFILE(DBG) GOTO EXIT_1; READ FILE(DBG) KEY('@') INTO(DB_RECORD); LETTERS = SUBSTR(DBKEY,2,1); DO WHILE(TRUE); READ FILE(DBG) INTO(DB_RECORD); LETTERS = TRIM(LETTERS) ||SUBSTR(DBKEY,2,1); END; EXIT_1: CLOSE FILE(DBG); PUT EDIT(LETTERS)(A); IF LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' THEN PUT SKIP LIST('TEST PASSES'); ELSE PUT SKIP LIST('TEST FAILS'); PUT SKIP; END TEST;