Relative & Indexed Files (VSAM)

22.1 File organizations compared

VSAM type (z/OS) ESDS (Entry-Sequenced) or QSAM Sequential (ESDS also by RBA) Logs, transaction journals RRDS (Relative Record) By record number 1..n

Slot-based tables, direct lookup by number KSDS (Key-Sequenced)

By key, sequentially in key order, or both Master files: customers, accounts, products

22.2 Indexed file operations

To read by the alternate key, move a value to ACCT-NAME and code READ ACCT-FILE KEY IS ACCT-NAME; READ NEXT then continues in name order.

With ACCESS MODE IS SEQUENTIAL, records must be written in ascending key order (status 21 otherwise). With DYNAMIC or RANDOM they can be written in any order (shown above).

INVALID KEY handles statuses 2x (duplicate key, not found); check FILE STATUS for everything else.

In SEQUENTIAL access, REWRITE/DELETE require a preceding successful READ (status 43 otherwise).

GnuCOBOL needs an indexed-file handler (Berkeley DB, VBISAM, or LMDB) — cobc --info shows which one is built in.

IDENTIFICATION DIVISION.
       PROGRAM-ID. ACCTIDX.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT ACCT-FILE ASSIGN TO "accounts.dat"
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS ACCT-ID
               ALTERNATE RECORD KEY IS ACCT-NAME WITH DUPLICATES
               FILE STATUS IS WS-FS.
       DATA DIVISION.
       FILE SECTION.
       FD  ACCT-FILE.
       01  ACCT-REC.
           05  ACCT-ID           PIC 9(6).
           05  ACCT-NAME         PIC X(20).
           05  ACCT-BALANCE      PIC S9(9)V99 COMP-3.
       WORKING-STORAGE SECTION.
       01  WS-FS                 PIC XX.
       01  WS-EOF                PIC X VALUE "N".
       01  ED-BAL                PIC -ZZZ,ZZZ,ZZ9.99.
       PROCEDURE DIVISION.
       0000-MAIN.
      *    Create the file and load three records
           OPEN OUTPUT ACCT-FILE
           MOVE 100200 TO ACCT-ID  MOVE "BAKER"  TO ACCT-NAME
           MOVE 1500.00 TO ACCT-BALANCE  PERFORM 8000-WRITE
           MOVE 100100 TO ACCT-ID  MOVE "ADAMS"  TO ACCT-NAME
           MOVE 250.50 TO ACCT-BALANCE   PERFORM 8000-WRITE
           MOVE 100300 
…
100100 ADAMS                         250.50
100200 BAKER                       1,600.00

22.3 Relative files

The RELATIVE KEY must be defined in WORKING-STORAGE (not in the record) and holds the record slot number.

SELECT SEAT-FILE ASSIGN TO "seats.dat"
               ORGANIZATION IS RELATIVE
               ACCESS MODE IS RANDOM
               RELATIVE KEY IS WS-SEAT-NO
               FILE STATUS IS WS-FS.
      * ...
       WORKING-STORAGE SECTION.
       01  WS-SEAT-NO            PIC 9(4).
      * ...
           MOVE 17 TO WS-SEAT-NO
           READ SEAT-FILE
               INVALID KEY DISPLAY "SEAT 17 EMPTY"
           END-READ

22.4 Defining VSAM datasets on z/OS (IDCAMS)

INDEXED = KSDS, NONINDEXED = ESDS, NUMBERED = RRDS, LINEAR = LDS.

KEYS(length offset) — offset is 0-based.

Alternate keys need DEFINE ALTERNATEINDEX, DEFINE PATH, and BLDINDEX; in JCL the path's DD name is the base DD name with a digit appended (e.g., ACCTFILE → ACCTFIL1).

In COBOL, an ESDS is ORGANIZATION SEQUENTIAL with ASSIGN TO AS-ddname; a KSDS is ORGANIZATION INDEXED; an RRDS is ORGANIZATION RELATIVE.

VSAM-specific extended status: add a second field FILE STATUS IS WS-FS WS-VSAM-FS where WS-VSAM-FS is a 6-byte group of return code, function code, and feedback code (each PIC 9(2) COMP).

Other IDCAMS commands: REPRO (copy/load), PRINT (dump records), LISTCAT (catalog info), VERIFY.

//DEFKSDS  JOB (ACCT),'DEFINE KSDS',CLASS=A,MSGCLASS=X
//STEP1    EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DELETE USER01.ACCOUNT.KSDS CLUSTER PURGE
  SET MAXCC = 0
  DEFINE CLUSTER (NAME(USER01.ACCOUNT.KSDS)     -
                  INDEXED                        -
                  KEYS(6 0)                      -
                  RECORDSIZE(32 32)              -
                  FREESPACE(10 10)               -
                  CYLINDERS(1 1))                -
         DATA    (NAME(USER01.ACCOUNT.KSDS.DATA)) -
         INDEX   (NAME(USER01.ACCOUNT.KSDS.INDEX))
/*