25 min read

Sequential files, covered in earlier chapters, serve well when every record must be processed in order. But what happens when a bank teller needs to look up one customer out of ten million? Reading sequentially through millions of records to find a...

Chapter 12: Indexed File Processing (VSAM KSDS)

Introduction

Sequential files, covered in earlier chapters, serve well when every record must be processed in order. But what happens when a bank teller needs to look up one customer out of ten million? Reading sequentially through millions of records to find a single account would be unacceptably slow. This is the problem that indexed files solve.

An indexed file maintains an internal index structure -- much like the index at the back of a book -- that maps key values to record locations. Given a customer account number, the system consults the index and retrieves the record directly, without scanning the entire file. This is the foundation of nearly every online transaction processing (OLTP) system built on mainframes.

On IBM z/OS systems, indexed files are implemented through VSAM (Virtual Storage Access Method), specifically the KSDS (Key-Sequenced Data Set) organization. VSAM KSDS files are the workhorses of mainframe data storage, underpinning banking systems, insurance claims, inventory management, and countless other applications that require fast retrieval by key.

This chapter teaches you everything you need to know about creating, reading, updating, and deleting records in VSAM KSDS files using COBOL. You will learn the three access modes (sequential, random, and dynamic), master the START statement for positioning, understand alternate keys for multi-path access, and learn the IDCAMS utility commands that define and manage VSAM files on z/OS.


12.1 What Is an Indexed File?

An indexed file is a file organization that supports direct access by key value. Each record contains a designated field called the primary key that uniquely identifies it. The file system maintains an index that maps each key value to the physical location of its record.

Think of it like a database table with a primary key column. You can:

  • Read any record directly by supplying its key value
  • Read records sequentially in key order
  • Add new records anywhere in logical key sequence
  • Update existing records in place
  • Delete records by key

This combination of capabilities makes indexed files ideal for master files -- the central repositories of business data such as customer records, account balances, product catalogs, and employee information.

Indexed Files vs. Other Organizations

Feature Sequential Relative Indexed (KSDS)
Access by key No By relative record number By any defined key
Insert in middle No (append only) Yes (by number) Yes (by key value)
Delete records No (mark only) Yes Yes
Multiple access paths No No Yes (alternate keys)
Sequential processing Efficient Possible Efficient
Random processing Not practical Efficient Efficient
Key type None Numeric only Any field type

12.2 VSAM Overview on z/OS

Virtual Storage Access Method (VSAM) is IBM's access method for managing data on z/OS. VSAM provides three types of data set organizations:

  1. KSDS (Key-Sequenced Data Set) -- Records are stored and retrieved by a primary key field. This is the indexed file organization. Records are maintained in key sequence.

  2. ESDS (Entry-Sequenced Data Set) -- Records are stored in the order they are written, similar to sequential files. Records are accessed by their relative byte address (RBA).

  3. RRDS (Relative Record Data Set) -- Records are accessed by relative record number, similar to COBOL relative file organization.

This chapter focuses exclusively on KSDS, as it is by far the most commonly used VSAM organization and corresponds directly to COBOL's ORGANIZATION IS INDEXED.

KSDS Cluster Components

A VSAM KSDS consists of two physical components:

  • Data component -- Contains the actual records, stored in key sequence within control intervals.
  • Index component -- Contains the index structure that maps key values to record locations. The index has multiple levels (sequence set and index set) for efficient lookup.

Together, these two components form a cluster. When you define a VSAM file, you define the cluster, and VSAM creates both components.

Control Intervals and Control Areas

VSAM organizes data differently from traditional sequential files:

  • Control Interval (CI) -- The unit of data transfer between disk and memory. A CI contains one or more records plus control information. Think of it as VSAM's equivalent of a block, but more sophisticated. Typical CI sizes range from 2,048 to 32,768 bytes.

  • Control Area (CA) -- A group of control intervals. A CA typically corresponds to one track or one cylinder of disk space. When VSAM needs to split a CI that is full (to insert a new record), it redistributes records within the CA.

Understanding CIs and CAs is important for performance tuning, which we cover in Section 12.12.

Free Space

When defining a KSDS, you can specify free space at two levels:

  • CI free space -- Percentage of each CI left empty during initial load. This space accommodates future inserts without immediately causing CI splits.
  • CA free space -- Percentage of CAs left entirely empty. When a CI split requires space beyond the current CA, VSAM uses these reserved CAs.

For example, FREESPACE(20 10) means 20% of each CI and 10% of CAs are left free. We will see this in the IDCAMS DEFINE CLUSTER command.


12.3 The SELECT Clause for Indexed Files

The SELECT clause in the FILE-CONTROL paragraph is where you declare an indexed file and its access characteristics. Here is the complete syntax:

       FILE-CONTROL.
           SELECT file-name
               ASSIGN TO external-name
               ORGANIZATION IS INDEXED
               ACCESS MODE IS {SEQUENTIAL | RANDOM | DYNAMIC}
               RECORD KEY IS data-name-1
               [ALTERNATE RECORD KEY IS data-name-2
                   [WITH DUPLICATES]]
               [FILE STATUS IS data-name-3].

ORGANIZATION IS INDEXED

This clause tells the COBOL runtime that the file is an indexed file. On z/OS, this maps to a VSAM KSDS. On GnuCOBOL, this maps to the configured indexed file handler (typically Berkeley DB, VBISAM, or another backend).

ACCESS MODE

The access mode determines which I/O operations are available and how they behave:

ACCESS MODE IS SEQUENTIAL

Records are processed in primary key order. You can: - Read all records from beginning to end (or from a START position) - Write records in ascending key order during initial load - Use START to position within the file

This mode is used for batch reporting and initial file loading.

ACCESS MODE IS RANDOM

Records are accessed directly by key value. You can: - Read any record by moving a value to the key field and issuing READ - Write records in any order - Update (REWRITE) and delete (DELETE) records by key

This mode is used for online inquiry and transaction processing.

ACCESS MODE IS DYNAMIC

This mode combines sequential and random access in the same program. You can: - Perform random reads (READ file-name) - Perform sequential reads (READ file-name NEXT) - Use START to position for sequential reading - Switch freely between random and sequential operations

This is the most flexible mode and is used when a program needs both capabilities, such as a browse function that starts at a random position and then reads forward.

RECORD KEY IS

This clause identifies the primary key field. The data-name must be defined within the record description (01 level under the FD). The primary key:

  • Must be unique across all records
  • Is used by the index for record retrieval
  • Cannot be changed by a REWRITE operation

ALTERNATE RECORD KEY IS

This clause defines additional keys for accessing the same file through different fields. You can define multiple alternate keys. Each alternate key:

  • Provides an additional access path to the records
  • Can be unique or allow duplicates (WITH DUPLICATES)
  • Requires a separate alternate index (AIX) defined via IDCAMS on z/OS

FILE STATUS IS

This clause names a two-byte alphanumeric field that receives a status code after every I/O operation. Always use FILE STATUS. It is the primary mechanism for detecting and handling I/O errors in indexed file processing.

Complete SELECT Example

Here is a fully specified SELECT for a customer master file with two alternate keys:

       FILE-CONTROL.
           SELECT CUSTOMER-MASTER
               ASSIGN TO CUSTMAST
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS CM-CUSTOMER-ID
               ALTERNATE RECORD KEY IS CM-SSN
               ALTERNATE RECORD KEY IS CM-LAST-NAME
                   WITH DUPLICATES
               FILE STATUS IS WS-VSAM-STATUS.

12.4 The FD for Indexed Files

The FD (File Description) for an indexed file is similar to other file types. The key requirement is that the RECORD KEY field must be defined within the 01 level record description:

       FD  CUSTOMER-MASTER
           RECORD CONTAINS 200 CHARACTERS.
       01  CUSTOMER-MASTER-RECORD.
           05  CM-CUSTOMER-ID          PIC X(10).
           05  CM-LAST-NAME            PIC X(25).
           05  CM-FIRST-NAME           PIC X(20).
           05  CM-MIDDLE-INIT          PIC X(01).
           05  CM-ADDRESS-LINE-1       PIC X(30).
           05  CM-ADDRESS-LINE-2       PIC X(30).
           05  CM-CITY                 PIC X(20).
           05  CM-STATE                PIC X(02).
           05  CM-ZIP-CODE             PIC X(10).
           05  CM-PHONE-NUMBER         PIC X(15).
           05  CM-ACCOUNT-TYPE         PIC X(02).
           05  CM-BALANCE              PIC S9(09)V99.
           05  CM-OPEN-DATE            PIC X(10).
           05  CM-STATUS-CODE          PIC X(01).
           05  FILLER                  PIC X(13).

Important notes:

  • The RECORD KEY field (CM-CUSTOMER-ID) must be defined at the 05 or lower level within the 01 record.
  • On z/OS, the record size in the FD must match the RECORDSIZE specified in the IDCAMS DEFINE CLUSTER.
  • For VSAM files, RECORDING MODE and BLOCK CONTAINS clauses are not typically needed (VSAM handles its own blocking through CIs).

12.5 OPEN Modes for Indexed Files

Indexed files support four OPEN modes:

OPEN Mode READ WRITE REWRITE DELETE START
INPUT Yes No No No Yes
OUTPUT No Yes No No No
I-O Yes Yes Yes Yes Yes
EXTEND No Yes No No No

OPEN INPUT -- Read-only access. Use for reports and inquiries.

OPEN OUTPUT -- Write-only access. Creates a new file or replaces all existing data. Used for initial loading.

OPEN I-O -- Full read/write access. Required for master file maintenance (adding, updating, deleting records).

OPEN EXTEND -- Append-only access. Adds records after the highest existing key. Rarely used with indexed files since random WRITE under I-O mode is more flexible.


12.6 Creating an Indexed File (Initial Load)

The initial load of a VSAM KSDS typically uses ACCESS MODE IS SEQUENTIAL with OPEN OUTPUT. Records must be written in ascending primary key order.

The Process

  1. Define the VSAM cluster using IDCAMS (in JCL, before the COBOL program runs)
  2. Open the KSDS for OUTPUT
  3. Read records from a sorted sequential input file
  4. Write each record to the KSDS
  5. Handle INVALID KEY conditions (duplicate keys, sequence errors)

Example 1: Creating an Indexed File

See code/example-01-create-indexed.cob for the complete program.

The critical code for writing records:

           WRITE CUSTOMER-MASTER-RECORD
               INVALID KEY
                   ADD 1 TO WS-RECORDS-ERROR
                   DISPLAY 'WRITE ERROR FOR KEY: '
                           CM-CUSTOMER-ID
                           ' STATUS: ' WS-MASTER-STATUS
               NOT INVALID KEY
                   ADD 1 TO WS-RECORDS-WRITTEN
           END-WRITE

The INVALID KEY clause fires when: - Status 21 -- Records are not in ascending key order - Status 22 -- A record with the same primary key already exists

JCL for Initial Load

See code/example-01-create-indexed.jcl for the complete JCL. The key element is the IDCAMS DEFINE CLUSTER:

  DEFINE CLUSTER (
           NAME(USERHLQ.CUSTOMER.MASTER)
           INDEXED
           KEYS(10 0)
           RECORDSIZE(200 200)
           FREESPACE(20 10)
           SHAREOPTIONS(2 3)
           SPEED
         )
         DATA (
           NAME(USERHLQ.CUSTOMER.MASTER.DATA)
           CYLINDERS(5 1)
           CISZ(4096)
         )
         INDEX (
           NAME(USERHLQ.CUSTOMER.MASTER.INDEX)
           TRACKS(3 1)
           CISZ(2048)
         )

Key parameters: - KEYS(10 0) -- 10-byte key starting at offset 0 - RECORDSIZE(200 200) -- Fixed-length 200-byte records (average and maximum are the same) - FREESPACE(20 10) -- 20% CI free space, 10% CA free space - SPEED -- Skip preformatting during initial load (faster) - CISZ(4096) -- Control interval size of 4096 bytes

Note that no DCB parameters are needed in the JCL DD statement for VSAM files. VSAM gets its attributes from the catalog, not from JCL.


12.7 Sequential Reading (ACCESS MODE IS SEQUENTIAL)

Sequential access reads records in primary key order, from lowest to highest. This is the most efficient way to process all records or a range of records.

Basic Sequential Read

Without a START statement, reading begins at the first record:

           OPEN INPUT CUSTOMER-MASTER

           PERFORM UNTIL END-OF-FILE
               READ CUSTOMER-MASTER NEXT
                   AT END
                       SET END-OF-FILE TO TRUE
                   NOT AT END
                       PERFORM PROCESS-RECORD
               END-READ
           END-PERFORM

Note: When ACCESS MODE IS SEQUENTIAL, you may omit the NEXT keyword. The following two statements are equivalent in sequential mode:

           READ CUSTOMER-MASTER
           READ CUSTOMER-MASTER NEXT

The START Statement

The START statement positions the file pointer without reading a record. After a successful START, the next READ (or READ NEXT) retrieves the record at the positioned location.

       SELECT CUSTOMER-MASTER
           ...
           ACCESS MODE IS SEQUENTIAL
           RECORD KEY IS CM-CUSTOMER-ID
           ...
           MOVE 'C000050000' TO CM-CUSTOMER-ID

           START CUSTOMER-MASTER
               KEY IS NOT LESS THAN CM-CUSTOMER-ID
               INVALID KEY
                   DISPLAY 'NO RECORDS AT OR BEYOND THIS KEY'
               NOT INVALID KEY
                   CONTINUE
           END-START

START supports several comparison operators:

Syntax Meaning
KEY IS EQUAL TO Position at exact key match
KEY IS GREATER THAN Position at first key greater than the given value
KEY IS NOT LESS THAN Position at first key greater than or equal to (most common)
KEY IS GREATER THAN OR EQUAL TO Same as NOT LESS THAN

START does not read a record. It only positions the file pointer. You must follow START with a READ to retrieve the record.

Example 2: Sequential Read with START

See code/example-02-sequential-read.cob for a complete program that reads a VSAM KSDS sequentially from a starting position and produces a formatted report.


12.8 Random Reading (ACCESS MODE IS RANDOM)

Random access retrieves a single record directly by its key value. This is the mode used for online inquiry and transaction processing.

The Random READ

To read randomly:

  1. Move the desired key value to the RECORD KEY field
  2. Issue a READ statement
  3. Check the file status or INVALID KEY condition
           MOVE 'C000025000' TO CM-CUSTOMER-ID

           READ CUSTOMER-MASTER
               INVALID KEY
                   DISPLAY 'RECORD NOT FOUND'
               NOT INVALID KEY
                   DISPLAY 'FOUND: ' CM-LAST-NAME
           END-READ

After a successful random READ, the entire record is available in the record area. The file status will be '00'.

If the record does not exist, the file status will be '23' (record not found), and the INVALID KEY clause executes.

Example 3: Random Read by Primary Key

See code/example-03-random-read.cob for a complete program that processes an inquiry file, looking up each customer by account number.


12.9 Dynamic Access (ACCESS MODE IS DYNAMIC)

Dynamic access mode is the most powerful and flexible. It allows you to mix random and sequential operations in the same program, making it ideal for browse functions and programs that need both lookup and scan capabilities.

In dynamic mode, the type of READ determines the access:

      * RANDOM READ -- retrieves record by key
           MOVE 'C000050000' TO CM-CUSTOMER-ID
           READ CUSTOMER-MASTER
               INVALID KEY ...
           END-READ

      * SEQUENTIAL READ -- retrieves next record in sequence
           READ CUSTOMER-MASTER NEXT
               AT END ...
           END-READ

The distinction is important: - READ file-name (without NEXT) performs a random read using the current key value - READ file-name NEXT performs a sequential read of the next record in key order

Browse Pattern

A common pattern is the "position and browse":

  1. Use START or random READ to position at a starting point
  2. Use READ NEXT in a loop to browse forward
  3. Use random READ again to jump to a different position
      * Position at the starting key
           MOVE WS-START-KEY TO CM-CUSTOMER-ID

           START CUSTOMER-MASTER
               KEY IS NOT LESS THAN CM-CUSTOMER-ID
               INVALID KEY
                   SET BROWSE-DONE TO TRUE
           END-START

      * Browse forward for N records
           PERFORM VARYING WS-COUNT FROM 1 BY 1
               UNTIL WS-COUNT > 10 OR BROWSE-DONE
               READ CUSTOMER-MASTER NEXT
                   AT END
                       SET BROWSE-DONE TO TRUE
                   NOT AT END
                       PERFORM DISPLAY-RECORD
               END-READ
           END-PERFORM

Example 4: Dynamic Access Mode

See code/example-04-dynamic-access.cob for a complete program demonstrating mixed random and sequential access, simulating an online browse function.


12.10 Updating and Deleting Records

Master file maintenance requires the ability to modify existing records and remove records that are no longer needed.

REWRITE (Update)

The REWRITE statement replaces an existing record with modified data. There is a critical rule: you must READ the record before you can REWRITE it.

The sequence is:

  1. READ the record (to establish the current record position)
  2. Modify the fields as needed
  3. REWRITE the record
      * Step 1: Read the existing record
           MOVE 'C000025000' TO CM-CUSTOMER-ID
           READ CUSTOMER-MASTER
               INVALID KEY
                   DISPLAY 'RECORD NOT FOUND'
                   GO TO UPDATE-EXIT
           END-READ

      * Step 2: Modify fields
           MOVE '555-123-4567' TO CM-PHONE-NUMBER
           MOVE 'A'            TO CM-STATUS-CODE

      * Step 3: Rewrite the modified record
           REWRITE CUSTOMER-MASTER-RECORD
               INVALID KEY
                   DISPLAY 'REWRITE FAILED: ' WS-VSAM-STATUS
               NOT INVALID KEY
                   DISPLAY 'RECORD UPDATED SUCCESSFULLY'
           END-REWRITE

You cannot change the primary key with REWRITE. If you need to change a record's key, you must DELETE the old record and WRITE a new one.

If you attempt a REWRITE without a prior successful READ, you will receive file status '43'.

DELETE

The DELETE statement removes a record from the file. In ACCESS MODE IS RANDOM or DYNAMIC, you can delete by key:

           MOVE 'C000025000' TO CM-CUSTOMER-ID

           DELETE CUSTOMER-MASTER
               INVALID KEY
                   DISPLAY 'DELETE FAILED - RECORD NOT FOUND'
               NOT INVALID KEY
                   DISPLAY 'RECORD DELETED'
           END-DELETE

In ACCESS MODE IS SEQUENTIAL, DELETE removes the record most recently read. A prior READ is required.

In ACCESS MODE IS RANDOM, you can delete without a prior READ -- just move the key value and issue DELETE. However, reading first is good practice for audit logging.

WRITE for Adding Records

In ACCESS MODE IS RANDOM or DYNAMIC with OPEN I-O, you can add new records using WRITE:

           MOVE 'C000099999' TO CM-CUSTOMER-ID
           MOVE 'NEW'        TO CM-LAST-NAME
           ...

           WRITE CUSTOMER-MASTER-RECORD
               INVALID KEY
                   DISPLAY 'ADD FAILED: ' WS-VSAM-STATUS
               NOT INVALID KEY
                   DISPLAY 'RECORD ADDED'
           END-WRITE

Status '22' indicates a duplicate primary key. In random mode, records can be added in any key order.

Example 5: Update and Delete Operations

See code/example-05-update-delete.cob for a complete master file maintenance program that processes add, update, and delete transactions with full audit logging.


12.11 Alternate Keys

Alternate keys provide additional access paths to the same file. While the primary key is typically an account number or ID, alternate keys let you retrieve records by other fields such as name, Social Security number, or zip code.

Defining Alternate Keys in COBOL

Alternate keys are declared in the SELECT clause:

           SELECT CUSTOMER-MASTER
               ASSIGN TO CUSTMAST
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS CM-CUSTOMER-ID
               ALTERNATE RECORD KEY IS CM-SSN
               ALTERNATE RECORD KEY IS CM-LAST-NAME
                   WITH DUPLICATES
               FILE STATUS IS WS-VSAM-STATUS.
  • CM-SSN is a unique alternate key (no two records can share the same SSN)
  • CM-LAST-NAME is a non-unique alternate key (multiple customers may share a last name), indicated by WITH DUPLICATES

Reading by Alternate Key

To read using an alternate key, use the KEY IS clause on the READ statement:

      * Read by SSN (alternate key)
           MOVE '123-45-6789' TO CM-SSN

           READ CUSTOMER-MASTER
               KEY IS CM-SSN
               INVALID KEY
                   DISPLAY 'SSN NOT FOUND'
               NOT INVALID KEY
                   DISPLAY 'FOUND: ' CM-CUSTOMER-ID
                           ' ' CM-LAST-NAME
           END-READ

Browsing by Alternate Key

You can START and READ NEXT using an alternate key. This is particularly useful for non-unique keys:

      * Position using last name
           MOVE 'SMITH' TO CM-LAST-NAME

           START CUSTOMER-MASTER
               KEY IS NOT LESS THAN CM-LAST-NAME
               INVALID KEY
                   DISPLAY 'NO MATCHING NAMES'
           END-START

      * Read all records with last name 'SMITH'
           PERFORM UNTIL WS-DONE = 'Y'
               READ CUSTOMER-MASTER NEXT
                   AT END
                       MOVE 'Y' TO WS-DONE
                   NOT AT END
                       IF CM-LAST-NAME NOT = 'SMITH'
                           MOVE 'Y' TO WS-DONE
                       ELSE
                           PERFORM DISPLAY-RECORD
                       END-IF
               END-READ
           END-PERFORM

File Status '02' -- Duplicate Alternate Key

When writing a record whose alternate key value already exists in the file (for a key defined WITH DUPLICATES), the file status is '02' rather than '00'. This is a successful completion -- the record was written -- but the status indicates that a duplicate alternate key value exists. Your program should treat '02' as a success:

       01  WS-VSAM-STATUS     PIC X(02).
           88  VSAM-OK        VALUE '00'.
           88  VSAM-DUP-ALT   VALUE '02'.

      * After WRITE or REWRITE:
           IF VSAM-OK OR VSAM-DUP-ALT
               ADD 1 TO WS-SUCCESS-COUNT
           ELSE
               PERFORM ERROR-HANDLER
           END-IF

VSAM Alternate Index Setup

On z/OS, alternate keys require additional VSAM objects defined via IDCAMS:

  1. DEFINE ALTERNATEINDEX -- Creates the AIX structure
  2. DEFINE PATH -- Creates a logical connection between the AIX and the base cluster
  3. BLDINDEX -- Builds the AIX from existing base cluster data

Example 6: Alternate Keys

See code/example-06-alternate-keys.cob for a complete program using alternate keys, and code/example-06-alternate-keys.jcl for the complete JCL including DEFINE ALTERNATEINDEX, DEFINE PATH, and BLDINDEX commands.


12.12 File Status Codes for Indexed Files

File status codes are your primary tool for detecting and handling I/O errors. Always define a FILE STATUS field and check it after every I/O operation.

Common Status Codes

Status Meaning Typical Cause
00 Successful completion Operation completed normally
02 Successful, duplicate alternate key WRITE/REWRITE created a duplicate value in a non-unique AIX
10 End of file READ NEXT reached beyond the last record
21 Key sequence error WRITE in sequential mode with out-of-order key
22 Duplicate primary key WRITE attempted with a key that already exists
23 Record not found READ, START, or DELETE for a key that does not exist
24 Key boundary violation WRITE attempted beyond the defined key range or file full
30 Permanent I/O error Hardware or media failure
35 File not found OPEN failed because file does not exist
37 File attribute mismatch OPEN mode conflicts with file organization
39 File attribute conflict Program's file description conflicts with actual file
41 File already open OPEN attempted on an already-open file
42 File not open I/O attempted on a file that is not open
43 No prior READ for REWRITE/DELETE REWRITE or DELETE attempted without a prior successful READ (sequential mode)
46 No valid next record READ NEXT failed because no valid position exists
47 READ not permitted File not opened for INPUT or I-O
48 WRITE not permitted File not opened for OUTPUT, I-O, or EXTEND
49 REWRITE/DELETE not permitted File not opened for I-O

Best Practice: Comprehensive Status Checking

       01  WS-VSAM-STATUS          PIC X(02).
           88  VSAM-OK             VALUE '00'.
           88  VSAM-DUP-ALT-KEY   VALUE '02'.
           88  VSAM-EOF            VALUE '10'.
           88  VSAM-KEY-SEQ-ERR   VALUE '21'.
           88  VSAM-DUP-PRI-KEY   VALUE '22'.
           88  VSAM-REC-NOT-FOUND VALUE '23'.
           88  VSAM-BOUNDARY-ERR  VALUE '24'.
           88  VSAM-PERM-ERROR    VALUE '30'.
           88  VSAM-FILE-NOT-FOUND VALUE '35'.

Check the status after every I/O operation:

           READ CUSTOMER-MASTER
               INVALID KEY
                   PERFORM HANDLE-READ-ERROR
               NOT INVALID KEY
                   PERFORM PROCESS-RECORD
           END-READ

      *    Additional check for unexpected statuses
           IF WS-VSAM-STATUS NOT = '00' AND
              WS-VSAM-STATUS NOT = '23'
               DISPLAY 'UNEXPECTED STATUS: ' WS-VSAM-STATUS
               PERFORM ABORT-PROGRAM
           END-IF

12.13 INVALID KEY and NOT INVALID KEY

The INVALID KEY clause is an inline error handler for indexed file operations. It executes when an I/O operation fails due to a key-related condition.

Which Operations Support INVALID KEY?

Statement INVALID KEY fires when...
READ (random) Record not found (status 23)
WRITE Duplicate key (22), sequence error (21), boundary (24)
REWRITE Key changed (21), or other error
DELETE Record not found (23)
START No record satisfies the condition (23)

INVALID KEY vs. FILE STATUS

Both mechanisms detect errors, but they serve different purposes:

  • INVALID KEY handles expected key-related errors inline
  • FILE STATUS catches all errors, including unexpected ones

Best practice is to use both:

           READ CUSTOMER-MASTER
               INVALID KEY
                   PERFORM HANDLE-NOT-FOUND
               NOT INVALID KEY
                   PERFORM PROCESS-RECORD
           END-READ

      *    Catch any unexpected status
           IF WS-VSAM-STATUS NOT = '00' AND
              WS-VSAM-STATUS NOT = '23'
               PERFORM UNEXPECTED-ERROR
           END-IF

NOT INVALID KEY

The NOT INVALID KEY clause executes when the operation succeeds (no key error). It is the positive counterpart to INVALID KEY:

           WRITE CUSTOMER-MASTER-RECORD
               INVALID KEY
                   PERFORM WRITE-FAILED
               NOT INVALID KEY
                   PERFORM WRITE-SUCCEEDED
           END-WRITE

12.14 VSAM on z/OS: IDCAMS Utility

IDCAMS (Integrated Data Catalog Access Method Services) is the z/OS utility for managing VSAM files. You use IDCAMS commands in JCL to define, delete, copy, list, and print VSAM data sets.

DEFINE CLUSTER

Creates a new VSAM cluster:

  DEFINE CLUSTER (
           NAME(USERHLQ.CUSTOMER.MASTER)
           INDEXED
           KEYS(10 0)
           RECORDSIZE(200 200)
           FREESPACE(20 10)
           SHAREOPTIONS(2 3)
         )
         DATA (
           NAME(USERHLQ.CUSTOMER.MASTER.DATA)
           CYLINDERS(5 1)
           CISZ(4096)
         )
         INDEX (
           NAME(USERHLQ.CUSTOMER.MASTER.INDEX)
           TRACKS(3 1)
           CISZ(2048)
         )

Key parameters:

Parameter Description
NAME Cluster name (up to 44 characters)
INDEXED KSDS organization
KEYS(length offset) Primary key: length in bytes and starting offset
RECORDSIZE(avg max) Average and maximum record sizes
FREESPACE(ci% ca%) Free space percentages
SHAREOPTIONS(cr cs) Cross-region and cross-system sharing options
SPEED/RECOVERY Skip or include preformatting
CYLINDERS/TRACKS(pri sec) Space allocation
CISZ Control interval size

DELETE

Removes a VSAM cluster:

  DELETE USERHLQ.CUSTOMER.MASTER
         CLUSTER
         PURGE

The PURGE option overrides any retention period. The common pattern for "delete if exists" is:

  DELETE USERHLQ.CUSTOMER.MASTER CLUSTER PURGE
  IF LASTCC = 8 THEN SET MAXCC = 0

The IF LASTCC = 8 handles the case where the cluster does not exist (which returns condition code 8) and resets the maximum condition code to 0 so the job continues.

REPRO

Copies data between data sets. Commonly used for backup and restore:

  REPRO INFILE(VSAMDD)
        OUTFILE(SEQDD)

Or to load a KSDS from a sequential file:

  REPRO INFILE(SEQDD)
        OUTFILE(VSAMDD)
        REPLACE

LISTCAT

Displays catalog information about a VSAM cluster:

  LISTCAT ENTRIES(USERHLQ.CUSTOMER.MASTER)
          ALL

The ALL option shows complete statistics including record counts, space usage, CI/CA split counts, and more. This is invaluable for monitoring file health.

PRINT

Displays records from a VSAM file:

  PRINT INFILE(VSAMDD)
        COUNT(10)
        CHARACTER

Options include CHARACTER, HEX, and DUMP format.

VERIFY

Corrects the catalog information after an abnormal program termination:

  VERIFY FILE(VSAMDD)

If a program abends while a VSAM file is open, the end-of-data information in the catalog may be incorrect. VERIFY corrects this.

DEFINE ALTERNATEINDEX

Creates an alternate index:

  DEFINE ALTERNATEINDEX (
           NAME(USERHLQ.CUSTOMER.MASTER.AIX.SSN)
           RELATE(USERHLQ.CUSTOMER.MASTER)
           KEYS(11 56)
           UNIQUEKEY
           UPGRADE
         )
Parameter Description
RELATE Names the base cluster
KEYS(length offset) AIX key position within the base record
UNIQUEKEY/NONUNIQUEKEY Whether duplicates are allowed
UPGRADE Keep AIX in sync when base changes

DEFINE PATH

Creates the logical connection between an AIX and its base cluster:

  DEFINE PATH (
           NAME(USERHLQ.CUSTOMER.MASTER.PATH.SSN)
           PATHENTRY(USERHLQ.CUSTOMER.MASTER.AIX.SSN)
           UPDATE
         )

BLDINDEX

Builds an alternate index from existing base cluster data:

  BLDINDEX INFILE(BASEDD)
           OUTFILE(AIXDD)
           INTERNALSORT

12.15 JCL for VSAM Files

VSAM files require different JCL considerations than traditional sequential files:

No DCB Parameters

VSAM files get their attributes from the VSAM catalog, not from JCL. A VSAM DD statement is simply:

//CUSTMAST DD DSN=USERHLQ.CUSTOMER.MASTER,DISP=SHR

There is no DCB parameter, no SPACE parameter, and no RECFM/LRECL/BLKSIZE. All of these are defined in the IDCAMS DEFINE CLUSTER and stored in the catalog.

DISP for VSAM

VSAM files always use DISP=SHR or DISP=OLD: - DISP=SHR -- Allows shared access (most common) - DISP=OLD -- Exclusive access

You never use DISP=(NEW,CATLG) for VSAM files because they are defined and cataloged by IDCAMS, not by JCL.

Alternate Key DDs

When a program opens a VSAM file with alternate keys, the JCL must include DD statements for the base cluster and each PATH, concatenated:

//CUSTMAST DD DSN=USERHLQ.CUSTOMER.MASTER,DISP=SHR
//         DD DSN=USERHLQ.CUSTOMER.MASTER.PATH.SSN,DISP=SHR
//         DD DSN=USERHLQ.CUSTOMER.MASTER.PATH.NAME,DISP=SHR

The PATH DDs must appear in the same order as the ALTERNATE RECORD KEY clauses in the SELECT statement.

Define Before Use

A VSAM cluster must be defined (via IDCAMS DEFINE CLUSTER) before a COBOL program can open it. The typical JCL pattern is:

  1. Step 1: IDCAMS DELETE (ignore if not found)
  2. Step 2: IDCAMS DEFINE CLUSTER
  3. Step 3: Run the COBOL program

12.16 GnuCOBOL Indexed File Implementation

GnuCOBOL supports indexed files through pluggable backend handlers. The COBOL source code is the same; only the underlying storage mechanism differs.

Common Backends

  • Berkeley DB (BDB) -- The default on many GnuCOBOL installations. Provides full indexed file support.
  • VBISAM -- An open-source ISAM implementation that emulates mainframe-style indexed files.
  • Lightning Memory-Mapped Database (LMDB) -- A high-performance alternative.

Key Differences from z/OS VSAM

  1. No IDCAMS -- Files are created automatically when opened for OUTPUT. No separate definition step is needed.
  2. No JCL -- File assignments use environment variables or command-line parameters.
  3. No CI/CA concepts -- The backend handles storage internally.
  4. Alternate keys -- Support varies by backend. Berkeley DB supports alternate keys; some backends do not.

GnuCOBOL SELECT Example

The COBOL code is identical. The ASSIGN clause maps to an environment variable:

       SELECT CUSTOMER-MASTER
           ASSIGN TO "customer.dat"
           ORGANIZATION IS INDEXED
           ACCESS MODE IS DYNAMIC
           RECORD KEY IS CM-CUSTOMER-ID
           FILE STATUS IS WS-STATUS.

To compile and run:

cobc -x -o custprog example-01-create-indexed.cob
export DD_CUSTMAST="customer.dat"
./custprog

Free-Format Reference

In GnuCOBOL's free format, the same code can be written without column restrictions:

select customer-master
    assign to "customer.dat"
    organization is indexed
    access mode is dynamic
    record key is cm-customer-id
    file status is ws-status.

12.17 Performance Considerations

VSAM KSDS performance depends on several factors. Understanding these helps you design files that remain efficient as they grow.

CI/CA Splits

When a record is inserted into a CI that is full, VSAM performs a CI split: it divides the CI's records between the original CI and a new CI within the same CA. If the CA is also full, a CA split occurs, which is more expensive because it involves allocating new space.

Excessive splits degrade performance because: - Records become physically scattered (loss of sequential locality) - More I/O operations are needed to read a sequence of records - Index levels may increase

Prevention strategies: - Define adequate free space with FREESPACE parameter - Reorganize periodically (REPRO out and back in) - Monitor split counts with LISTCAT

Free Space Planning

Scenario Recommended FREESPACE
Read-only file (no inserts) (0 0)
Low insert activity (10 5)
Moderate insert activity (20 10)
Heavy random inserts (30 15)
Inserts concentrated at end (5 0)

More free space means fewer splits but more wasted disk space and potentially more I/O for sequential reads (because each CI holds fewer records).

Buffer Tuning

VSAM uses buffers (BUFND for data buffers, BUFNI for index buffers) to cache CI contents in memory. More buffers mean fewer physical I/O operations.

In JCL, you can override defaults:

//CUSTMAST DD DSN=USERHLQ.CUSTOMER.MASTER,DISP=SHR,
//            AMP=('BUFND=10,BUFNI=5')

Rules of thumb: - For sequential processing: increase BUFND (more data CIs in memory) - For random processing: increase BUFNI (more index levels cached) - Always cache the entire index set in memory if possible

CI Size Selection

Larger CIs hold more records and can be more efficient for sequential access, but they transfer more data per I/O operation and use more buffer space. Smaller CIs are better for random access with small records.

CI Size Best For
2,048 - 4,096 Random access, small records
4,096 - 8,192 Mixed workloads
8,192 - 32,768 Sequential access, batch processing

Alternate Index Overhead

Each UPGRADE alternate index adds overhead to every WRITE, REWRITE, and DELETE operation on the base cluster, because VSAM must also update each AIX. For write-heavy workloads:

  • Minimize the number of alternate indexes
  • Consider building AIX records in batch (non-UPGRADE) and rebuilding periodically
  • Use UPGRADE only for alternate keys that must be current for online access

12.18 Common Patterns

Pattern 1: Online Inquiry (Read by Key)

      * Open for INPUT, ACCESS MODE IS RANDOM
      * Loop: receive key from user, READ, display record
           MOVE WS-USER-KEY TO CM-CUSTOMER-ID
           READ CUSTOMER-MASTER
               INVALID KEY
                   DISPLAY 'NOT FOUND'
               NOT INVALID KEY
                   PERFORM DISPLAY-CUSTOMER
           END-READ

Pattern 2: Batch Update (Sequential Read + Random Update)

A program with two files open: the master file (I-O, RANDOM) and a sorted transaction file (INPUT, SEQUENTIAL):

      * Read transaction
      * Move key to master file key field
      * READ master (random)
      * Apply changes
      * REWRITE master

Pattern 3: Master File Maintenance

A complete cycle: 1. IDCAMS backup (REPRO to sequential) 2. COBOL program processes add/change/delete transactions 3. IDCAMS LISTCAT to verify results 4. Periodic reorganization (REPRO out and in with new FREESPACE)

Pattern 4: Range Processing

Read all records within a key range:

           MOVE WS-START-KEY TO CM-CUSTOMER-ID
           START CUSTOMER-MASTER
               KEY IS NOT LESS THAN CM-CUSTOMER-ID

           PERFORM UNTIL END-OF-RANGE
               READ CUSTOMER-MASTER NEXT
                   AT END SET END-OF-RANGE TO TRUE
                   NOT AT END
                       IF CM-CUSTOMER-ID > WS-END-KEY
                           SET END-OF-RANGE TO TRUE
                       ELSE
                           PERFORM PROCESS-RECORD
                       END-IF
               END-READ
           END-PERFORM

12.19 Error Handling for Indexed Files

Robust error handling is essential for indexed file programs. Here is a comprehensive approach:

Design Principles

  1. Always use FILE STATUS -- Define a status field and check it after every I/O operation.
  2. Use INVALID KEY for expected conditions -- Record not found, duplicate key, etc.
  3. Use FILE STATUS for unexpected conditions -- I/O errors, file not open, etc.
  4. Log all errors -- Write error details to an error file or log for later analysis.
  5. Include the key value and status code in every error message.

Error Recovery Strategies

Status Recovery
23 (not found) Log and continue; this may be normal
22 (duplicate key) Log and skip; consider as business error
21 (sequence error) Input data is not sorted; abort or skip
35 (file not found) VSAM cluster not defined; abort
30 (I/O error) Severe; abort and alert operations
43 (no prior read) Programming error; fix the logic

Declaratives (USE AFTER STANDARD ERROR)

COBOL provides a declarative error-handling mechanism. Declaratives are defined in the PROCEDURE DIVISION before the main logic:

       PROCEDURE DIVISION.
       DECLARATIVES.
       VSAM-ERROR SECTION.
           USE AFTER STANDARD ERROR PROCEDURE ON CUSTOMER-MASTER.
       VSAM-ERROR-HANDLER.
           DISPLAY 'VSAM ERROR ON CUSTOMER-MASTER'
           DISPLAY 'FILE STATUS: ' WS-VSAM-STATUS
           .
       END DECLARATIVES.

Declaratives are invoked automatically when an I/O error occurs that is not handled by INVALID KEY. They are useful as a safety net but should not replace explicit FILE STATUS checking.


12.20 Summary of I/O Statements for Indexed Files

Statement Access Mode Open Mode Description
READ file Random/Dynamic INPUT, I-O Read by primary key
READ file KEY IS alt-key Random/Dynamic INPUT, I-O Read by alternate key
READ file NEXT Sequential/Dynamic INPUT, I-O Read next in key sequence
WRITE record Sequential OUTPUT Write in key order (initial load)
WRITE record Random/Dynamic OUTPUT, I-O Write in any order
REWRITE record Any I-O Update after READ
DELETE file Random/Dynamic I-O Delete by key (no prior READ needed)
DELETE file Sequential I-O Delete last record read
START file Sequential/Dynamic INPUT, I-O Position for sequential read

12.21 Fixed-Format vs. Free-Format Reference

All examples in this chapter use traditional fixed-format COBOL (columns 7-72). Here is a comparison for key constructs:

Fixed format (z/OS standard):

       FILE-CONTROL.
           SELECT CUSTOMER-MASTER
               ASSIGN TO CUSTMAST
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS CM-CUSTOMER-ID
               ALTERNATE RECORD KEY IS CM-SSN
               FILE STATUS IS WS-VSAM-STATUS.

Free format (GnuCOBOL, COBOL 2002+):

file-control.
    select customer-master
        assign to "custmast"
        organization is indexed
        access mode is dynamic
        record key is cm-customer-id
        alternate record key is cm-ssn
        file status is ws-vsam-status.

The syntax is identical; only the column restrictions and case conventions differ. Free format allows code to start in any column and uses any case.


12.22 Chapter Summary

This chapter covered the complete lifecycle of indexed file processing in COBOL:

  • Indexed files provide random access by key, combining the flexibility of a database with the simplicity of file I/O.
  • VSAM KSDS is the z/OS implementation of indexed files, using a cluster of data and index components organized into control intervals and control areas.
  • Three access modes -- SEQUENTIAL for batch processing, RANDOM for direct access, and DYNAMIC for mixed operations.
  • I/O statements -- READ, WRITE, REWRITE, DELETE, and START each have specific rules and behaviors depending on access mode and open mode.
  • Alternate keys provide multiple access paths to the same file, with VSAM AIX and PATH objects supporting them on z/OS.
  • File status codes are essential for error detection, with codes like 00 (success), 23 (not found), and 22 (duplicate key) being the most common.
  • IDCAMS is the z/OS utility for defining, deleting, copying, listing, and printing VSAM data sets.
  • Performance depends on free space planning, CI/CA split management, buffer tuning, and alternate index strategy.

Mastering indexed file processing is a critical skill for any COBOL programmer working on mainframe systems. The concepts in this chapter form the foundation for building and maintaining the master files that drive enterprise business operations.