Indexed Record I/O

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:.

File Attributes Permitted Access
SEQUENTIAL SEQUENTIAL only
DIRECT KEYED only
KEYED SEQUENTIAL      SEQUENTIAL and/or KEYED in combination

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.

Option
Description
ASCII The ASCII option is assumed in Open PL/I.
BKWD Specifies backward processing for indexed files read in the SEQUENTIAL mode; that is, sequential processing will start with the current record (or last, by default) in the file and proceed to previous records.
BUFFERS The BUFFERS option is not needed in Open PL/I. If it is used, the Compiler ignores it.
BUFOFF The BUFOFF option is not needed in Open PL/I. If it is used, the Compiler ignores it.
BUFND The BUFND option is not needed in Open PL/I. If it is used, the Compiler ignores it.
BUFNI The BUFNI option is not needed in Open PL/I. If it is used, the Compiler ignores it.
BUFSP The BUFSP option is not needed in Open PL/I. If it is used, the Compiler ignores it.
CONSECUTIVE Specifies a file with consecutive organization. For related information, see the description of the -defext compiler option in your Open PL/I User's Guide.
CTLASA The CTLASA option is ignored.
CTL360 The CTL360 option is not implemented in Open PL/I. If it is used, the Compiler issues a warning.
EBCDIC Specifies that EBCDIC data is present.
F | FB | FS | FBS Specifies record format, where F = fixed length, B = blocked, and S = standard. Open PL/I treats any such specification as though it were F alone — fixed length.
GENKEY Specifies a generic key, a character string that is a prefix of a key. A READ with a KEY clause returns the first record whose key is greater than or equal to the string specified by the KEY clause. A subsequent sequential READ (no KEY clause) will return the next record greater than the current record.
GRAPHIC Specifies that GRAPHIC data is present.
INDEXAREA(n) The INDEXAREA option is not needed in Open PL/I. If it is used, the Compiler ignores it.
INDEXED Specifies a file with indexed organization. For related information, see the description of the -defext compiler option in your Open PL/I User's Guide.
KEYLENGTH(n) Specifies the length, n, of the embedded key for indexed files. KEYLENGTH can be specified only when INDEXED or VSAM is specified.
KEYLOC(n) Specifies the starting location of an embedded key in a record. n must be within the limits:

1 ≥ n ≥ recordsize – keylength + 1.

The default KEYLOC value is 1.

KEYLOC can be specified only when INDEXED or VSAM is specified.

Note:

Be sure that KEYLOC considers the 2-byte prefix generated by specification of the SCALARVARYING option.

LEAVE The LEAVE option is not needed in Open PL/I. If it is used, the Compiler ignores it.
MINRECSIZE(n) Specifies the minimum record length, n. n must be an integer. This option allows for better disk space utilization. The MINRECSIZE option is required with V (variable-length) record formats. It is ignored for F (fixed-length) record formats. The values specified for the KEYLOC and KEYLENGTH options must be within the value specified by the MINRECSIZE option. For more information on the MINRECSIZE option, see below.
NCP(n) The NCP option is not applicable in Open PL/I. Open PL/I treats INDEXED files the same as VSAM files.
NOWRITE The NOWRITE option is not needed in Open PL/I. If it is used, the Compiler ignores it.
PASSWORD(pwd) The PASSWORD option is not implemented in Open PL/I. If it is used, the Compiler issues a warning.
RECSIZE(n) Specifies the maximum record length, n. n must be a positive integer, or a static variable with an initializer. For the static variable case, the initial value will be used as the maximum fixed record size. Note that “dynamic” RECSIZE usage is not supported - if this is not needed, VALUE is preferable to STATIC INIT.
REGIONAL(1 | 2 | 3) The REGIONAL(1) option specifies files whose records are identified by their region numbers.

The REGIONAL(2) and REGIONAL(3) options are not implemented in Open PL/I. If they are used, the Compiler flags it as an error.

REUSE The REUSE option specifies that an OUTPUT file associated with a VSAM data set is to be used as a work file. .
SCALARVARYING Used for the input and output of varying length records. SCALARVARYING enables the recognition of a two-byte prefix that specifies the current length of the records.
Note:

Be sure that KEYLOC considers the 2-byte prefix generated by specification of the SCALARVARYING option.

SIS The SIS option is not needed in Open PL/I. If it is used, the Compiler ignores it.
SKIP The SKIP option is not needed in Open PL/I. If it is used, the Compiler ignores it.
TP(MIR) The TP(MIR) option is not implemented in Open PL/I. If it is used, the Compiler flags it as an error.
TRKOFL The TRKOFL option is not needed in Open PL/I. If it is used, the Compiler ignores it.
U | D | DB Specifies record format. Open PL/I treats any such specification as though it were F – fixed length.
V | VB | VS | VBS Specifies record format, where V = variable length, B = blocked, and S = spanned. Open PL/I treats any such specification as though it were V.
VSAM Specifies files organized for the virtual storage access method (VSAM). For related information, see the description of the -defext compiler option in your Open PL/I User's Guide.

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;