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...
In This Chapter
- Introduction
- 12.1 What Is an Indexed File?
- 12.2 VSAM Overview on z/OS
- 12.3 The SELECT Clause for Indexed Files
- 12.4 The FD for Indexed Files
- 12.5 OPEN Modes for Indexed Files
- 12.6 Creating an Indexed File (Initial Load)
- 12.7 Sequential Reading (ACCESS MODE IS SEQUENTIAL)
- 12.8 Random Reading (ACCESS MODE IS RANDOM)
- 12.9 Dynamic Access (ACCESS MODE IS DYNAMIC)
- 12.10 Updating and Deleting Records
- 12.11 Alternate Keys
- 12.12 File Status Codes for Indexed Files
- 12.13 INVALID KEY and NOT INVALID KEY
- 12.14 VSAM on z/OS: IDCAMS Utility
- 12.15 JCL for VSAM Files
- 12.16 GnuCOBOL Indexed File Implementation
- 12.17 Performance Considerations
- 12.18 Common Patterns
- 12.19 Error Handling for Indexed Files
- 12.20 Summary of I/O Statements for Indexed Files
- 12.21 Fixed-Format vs. Free-Format Reference
- 12.22 Chapter Summary
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:
-
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.
-
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).
-
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 MODEandBLOCK CONTAINSclauses 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
- Define the VSAM cluster using IDCAMS (in JCL, before the COBOL program runs)
- Open the KSDS for OUTPUT
- Read records from a sorted sequential input file
- Write each record to the KSDS
- 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:
- Move the desired key value to the RECORD KEY field
- Issue a READ statement
- 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.
Random READ vs. Sequential READ NEXT
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":
- Use START or random READ to position at a starting point
- Use READ NEXT in a loop to browse forward
- 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:
- READ the record (to establish the current record position)
- Modify the fields as needed
- 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-SSNis a unique alternate key (no two records can share the same SSN)CM-LAST-NAMEis a non-unique alternate key (multiple customers may share a last name), indicated byWITH 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:
- DEFINE ALTERNATEINDEX -- Creates the AIX structure
- DEFINE PATH -- Creates a logical connection between the AIX and the base cluster
- 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.
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:
- Step 1: IDCAMS DELETE (ignore if not found)
- Step 2: IDCAMS DEFINE CLUSTER
- 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
- No IDCAMS -- Files are created automatically when opened for OUTPUT. No separate definition step is needed.
- No JCL -- File assignments use environment variables or command-line parameters.
- No CI/CA concepts -- The backend handles storage internally.
- 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
- Always use FILE STATUS -- Define a status field and check it after every I/O operation.
- Use INVALID KEY for expected conditions -- Record not found, duplicate key, etc.
- Use FILE STATUS for unexpected conditions -- I/O errors, file not open, etc.
- Log all errors -- Write error details to an error file or log for later analysis.
- 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.