On a z/OS mainframe, every piece of persistent data -- whether it is a COBOL source program, a compiled load module, an account master file, or a printed report waiting for delivery -- is stored in a dataset. The term "dataset" is the z/OS...
In This Chapter
- Introduction: Data Lives in Datasets
- Dataset Organizations
- Dataset Naming Conventions
- Record Formats
- Space Allocation
- DASD Concepts
- SMS (Storage Management Subsystem)
- Generation Data Groups (GDG)
- Dataset Allocation in JCL -- The DD Statement
- Temporary Datasets and Instream Data
- COBOL Program Interaction with Datasets
- Common Errors and Troubleshooting
- Practical Reference: Dataset Type Selection Guide
- Advanced Topics
- Summary
- Key Terms
- Exercises
Chapter 30: z/OS Dataset Concepts and Storage Management
Introduction: Data Lives in Datasets
On a z/OS mainframe, every piece of persistent data -- whether it is a COBOL source program, a compiled load module, an account master file, or a printed report waiting for delivery -- is stored in a dataset. The term "dataset" is the z/OS equivalent of what other operating systems call a "file," but datasets carry far more metadata, have stricter organizational rules, and are managed by a sophisticated storage infrastructure that has evolved over six decades of continuous operation.
For a COBOL programmer, understanding datasets is not a peripheral skill. Every SELECT statement in a COBOL program maps to a dataset. Every READ, WRITE, REWRITE, and DELETE operates against a dataset. The JCL that runs your program must allocate datasets with the correct organization, record format, and space. When a batch job abends with a B37 (out of space), an S013 (conflicting DCB attributes), or an S213 (dataset not found), you need to understand the dataset concepts that underlie these failures in order to diagnose and fix them.
This chapter provides a complete treatment of z/OS dataset concepts from the COBOL programmer's perspective. You will learn the four fundamental dataset organizations, naming conventions, record formats, space allocation, DASD architecture, the Storage Management Subsystem, Generation Data Groups, JCL allocation parameters, and how COBOL programs interact with each dataset type through SELECT...ASSIGN and FD entries.
Prerequisites: This chapter builds directly on the file handling concepts from Chapters 11 through 13 (sequential, indexed, and relative file processing) and the JCL fundamentals from Chapter 27. Familiarity with those chapters will help you connect the COBOL language constructs to their z/OS implementation.
Dataset Organizations
z/OS supports four fundamental dataset organizations, each designed for specific access patterns. Choosing the correct organization is one of the most consequential decisions a COBOL programmer makes, because it determines how records are stored, how they can be accessed, and what COBOL file operations are permitted.
Physical Sequential (PS)
A Physical Sequential dataset stores records one after another in the order they were written. Records are accessed sequentially -- you read from the beginning and proceed through the dataset in order. There is no index, no key, and no mechanism for direct access to a specific record.
+------------------------------------------------------------------+
| Physical Sequential Dataset (PS) |
| |
| +--------+--------+--------+--------+--------+--------+------+ |
| | Rec 1 | Rec 2 | Rec 3 | Rec 4 | Rec 5 | Rec 6 | ... | |
| +--------+--------+--------+--------+--------+--------+------+ |
| |
| Records stored contiguously, read/written in order |
| No index, no keys, no direct access |
+------------------------------------------------------------------+
Sequential datasets are the workhorses of batch processing. They are used for:
- Transaction files -- daily batches of deposits, withdrawals, and transfers
- Report output -- printed statements, regulatory filings, audit trails
- Extract files -- data extracted from a database for downstream processing
- Sort work files -- intermediate files used during sort operations
- Log files -- chronological records of system or application events
JCL to create a sequential dataset:
//TRANSIN DD DSN=PROD.BANKING.DAILY.TRANS,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(10,5),RLSE),
// DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
COBOL SELECT and FD for a sequential dataset:
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT TRANSACTION-FILE
ASSIGN TO TRANSIN
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-TRANS-STATUS.
DATA DIVISION.
FILE SECTION.
FD TRANSACTION-FILE
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 200 CHARACTERS
LABEL RECORDS ARE STANDARD.
01 TRANSACTION-RECORD.
05 TR-ACCOUNT-NUMBER PIC X(10).
05 TR-TRANSACTION-TYPE PIC X(02).
05 TR-TRANSACTION-DATE PIC 9(08).
05 TR-TRANSACTION-AMOUNT PIC S9(13)V99 COMP-3.
05 TR-DESCRIPTION PIC X(40).
05 FILLER PIC X(132).
The ASSIGN TO TRANSIN clause links the COBOL file to the JCL DD name TRANSIN. The ORGANIZATION IS SEQUENTIAL clause (which is the default and can be omitted) tells COBOL this is a sequential file. BLOCK CONTAINS 0 RECORDS [IBM Enterprise COBOL] instructs the system to use the optimal block size, which is the recommended practice on z/OS.
Partitioned Organization (PO) -- PDS and PDSE
A Partitioned Dataset (PDS) is a dataset that contains a directory followed by one or more members. Each member is itself a sequential dataset, identified by a name of up to eight characters. The directory contains entries that map each member name to its location within the dataset.
+------------------------------------------------------------------+
| Partitioned Dataset (PDS / PDSE) |
| |
| +---------------------+ |
| | DIRECTORY | |
| |---------------------| |
| | ACCTMAST --> Block 5 | |
| | CUSTMAST --> Block 22| |
| | TRANRPT --> Block 41| |
| | VALIDATE --> Block 58| |
| +---------------------+ |
| |
| +----------+ +----------+ +----------+ +----------+ |
| | ACCTMAST | | CUSTMAST | | TRANRPT | | VALIDATE | |
| | (member) | | (member) | | (member) | | (member) | |
| | seq data | | seq data | | seq data | | seq data | |
| +----------+ +----------+ +----------+ +----------+ |
+------------------------------------------------------------------+
A PDSE (Partitioned Dataset Extended) is the modern replacement for a PDS. PDSEs eliminate several PDS limitations: they automatically reclaim space when members are deleted or replaced, they support members larger than 16 MB, and they allow concurrent updates to different members. On modern z/OS systems, PDSEs are the standard choice for new partitioned datasets.
Partitioned datasets are used for:
- Source libraries -- COBOL, JCL, copybooks, and procedure libraries
- Load libraries -- compiled and link-edited programs
- Control card libraries -- SYSIN data, utility control statements
- Copybook libraries -- COBOL COPY members referenced during compilation
JCL to create a PDSE:
//SRCLIB DD DSN=DEV.BANKING.COBOL.SOURCE,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(5,2,50)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// DSNTYPE=LIBRARY
The DSNTYPE=LIBRARY parameter [IBM Enterprise COBOL] creates a PDSE rather than a traditional PDS. The third subparameter of SPACE -- 50 in this example -- specifies the number of directory blocks for a PDS (each block holds approximately six member entries). For a PDSE, directory space is managed automatically, but the subparameter is still required syntactically.
COBOL SELECT for reading a PDS member:
When a COBOL program reads from a PDS or PDSE, it typically accesses a single member. The member name is specified on the JCL DD statement:
//CTLCARD DD DSN=PROD.BANKING.CONTROL(DAILYPARM),DISP=SHR
FILE-CONTROL.
SELECT CONTROL-FILE
ASSIGN TO CTLCARD
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-CTL-STATUS.
From COBOL's perspective, reading a PDS member is identical to reading a sequential dataset. The member selection is handled entirely by JCL.
Direct Access (DA)
Direct Access organization (also called BDAM -- Basic Direct Access Method) allows records to be accessed by their physical location on disk. This is the oldest random-access method on z/OS and has been largely superseded by VSAM. A COBOL programmer is unlikely to encounter new DA datasets, but they may exist in legacy applications.
COBOL's ORGANIZATION IS RELATIVE with ACCESS MODE IS RANDOM maps conceptually to direct access -- records are accessed by a relative record number that translates to a physical position.
In modern practice, VSAM RRDS (Relative Record Dataset) has replaced BDAM for relative record processing. New development should always use VSAM rather than DA organization.
VSAM (Virtual Storage Access Method)
VSAM is the most capable and widely used access method on z/OS for datasets that require keyed or direct access. VSAM datasets are defined using the IDCAMS utility (Access Method Services) rather than through JCL allocation parameters alone.
VSAM provides four dataset types, called cluster types:
+------------------------------------------------------------------+
| VSAM Cluster Types |
| |
| ESDS (Entry-Sequenced Data Set) |
| +--------+--------+--------+--------+--------+ |
| | Rec 1 | Rec 2 | Rec 3 | Rec 4 | Rec 5 | --> sequential |
| +--------+--------+--------+--------+--------+ by arrival |
| |
| KSDS (Key-Sequenced Data Set) |
| +--INDEX--+ +---------DATA---------+ |
| | Key-Ptr |---->| Rec(sorted by key) | |
| | Key-Ptr |---->| Rec(sorted by key) | |
| | Key-Ptr |---->| Rec(sorted by key) | |
| +---------+ +-----------------------+ |
| |
| RRDS (Relative Record Data Set) |
| +------+------+------+------+------+------+ |
| |Slot 1|Slot 2|Slot 3|empty |Slot 5|Slot 6| --> by slot number |
| +------+------+------+------+------+------+ |
| |
| LDS (Linear Data Set) |
| +------------------------------------------+ |
| | Byte-stream data (no record structure) | --> byte-stream |
| +------------------------------------------+ |
+------------------------------------------------------------------+
ESDS (Entry-Sequenced Data Set)
An ESDS stores records in the order they are written, much like a PS dataset. However, ESDS provides an advantage: each record has a Relative Byte Address (RBA) that can be used for direct access. Records cannot be deleted from an ESDS, and their length cannot change.
ESDS is commonly used for: - Transaction logs where chronological order must be preserved - Audit trails that must never have records removed - Journal files for database recovery
IDCAMS definition for an ESDS:
//DEFESDS EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE CLUSTER ( -
NAME(PROD.BANKING.TRANS.LOG) -
NONINDEXED -
RECORDS(100000 50000) -
RECORDSIZE(200 200) -
SHAREOPTIONS(2 3) -
) -
DATA ( -
NAME(PROD.BANKING.TRANS.LOG.DATA) -
CONTROLINTERVALSIZE(4096) -
)
/*
The keyword NONINDEXED specifies an ESDS. RECORDS(100000 50000) allocates space for a primary quantity of 100,000 records and a secondary extent of 50,000 records. RECORDSIZE(200 200) indicates fixed-length records of 200 bytes (average and maximum are the same).
COBOL for an ESDS:
FILE-CONTROL.
SELECT TRANSACTION-LOG
ASSIGN TO TRANSLOG
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-LOG-STATUS.
FILE SECTION.
FD TRANSACTION-LOG
RECORD CONTAINS 200 CHARACTERS.
01 LOG-RECORD.
05 LOG-TIMESTAMP PIC X(26).
05 LOG-TRANSACTION-ID PIC X(12).
05 LOG-ACCOUNT-NUMBER PIC X(10).
05 LOG-ACTION-CODE PIC X(02).
05 LOG-AMOUNT PIC S9(13)V99 COMP-3.
05 LOG-DESCRIPTION PIC X(50).
05 LOG-OPERATOR-ID PIC X(08).
05 FILLER PIC X(84).
In JCL, the DD for a VSAM dataset does not include DCB or SPACE parameters -- these are already defined in the VSAM catalog:
//TRANSLOG DD DSN=PROD.BANKING.TRANS.LOG,DISP=SHR
KSDS (Key-Sequenced Data Set)
A KSDS is the most important VSAM type for COBOL programmers. Records are stored in logical order by a primary key -- a field within each record that uniquely identifies it. A KSDS consists of two components: an index component that maps keys to physical locations, and a data component that holds the actual records.
KSDS supports: - Sequential access (records returned in key order) - Random access (direct retrieval by key) - Dynamic access (switch between sequential and random in the same program) - Alternate indexes (secondary keys for alternative access paths)
KSDS is the standard choice for master files in financial applications -- account master files, customer master files, product catalogs, and any file where records must be retrieved by a unique identifier.
IDCAMS definition for a KSDS:
//DEFKSDS EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE CLUSTER ( -
NAME(PROD.BANKING.ACCT.MASTER) -
INDEXED -
KEYS(10 0) -
RECORDS(500000 100000) -
RECORDSIZE(350 350) -
SHAREOPTIONS(2 3) -
FREESPACE(10 5) -
) -
INDEX ( -
NAME(PROD.BANKING.ACCT.MASTER.INDEX)-
CONTROLINTERVALSIZE(4096) -
) -
DATA ( -
NAME(PROD.BANKING.ACCT.MASTER.DATA) -
CONTROLINTERVALSIZE(8192) -
)
/*
Key parameters:
- INDEXED specifies a KSDS
- KEYS(10 0) means the key is 10 bytes long starting at offset 0 (the first byte of the record)
- FREESPACE(10 5) reserves 10% free space within each Control Interval and 5% of Control Intervals within each Control Area -- this distributed free space allows efficient insertion of new records
- CONTROLINTERVALSIZE (CI size) is the unit of I/O for VSAM, analogous to a block
COBOL for a KSDS with dynamic access:
FILE-CONTROL.
SELECT ACCOUNT-MASTER
ASSIGN TO ACCTMAST
ORGANIZATION IS INDEXED
ACCESS MODE IS DYNAMIC
RECORD KEY IS AM-ACCOUNT-NUMBER
FILE STATUS IS WS-ACCT-STATUS.
FILE SECTION.
FD ACCOUNT-MASTER
RECORD CONTAINS 350 CHARACTERS.
01 ACCOUNT-MASTER-RECORD.
05 AM-ACCOUNT-NUMBER PIC X(10).
05 AM-CUSTOMER-NAME PIC X(30).
05 AM-ACCOUNT-TYPE PIC X(02).
88 AM-CHECKING VALUE 'CK'.
88 AM-SAVINGS VALUE 'SV'.
88 AM-MONEY-MARKET VALUE 'MM'.
88 AM-CERTIFICATE VALUE 'CD'.
05 AM-OPEN-DATE PIC 9(08).
05 AM-CURRENT-BALANCE PIC S9(13)V99 COMP-3.
05 AM-AVAILABLE-BALANCE PIC S9(13)V99 COMP-3.
05 AM-INTEREST-RATE PIC 9(03)V9(05) COMP-3.
05 AM-LAST-ACTIVITY-DATE PIC 9(08).
05 AM-STATUS-CODE PIC X(01).
88 AM-ACTIVE VALUE 'A'.
88 AM-CLOSED VALUE 'C'.
88 AM-FROZEN VALUE 'F'.
05 AM-BRANCH-CODE PIC X(04).
05 FILLER PIC X(275).
The RECORD KEY IS AM-ACCOUNT-NUMBER clause identifies the primary key field. This must correspond to the key definition in the IDCAMS KEYS parameter -- both must agree on the key's position and length.
RRDS (Relative Record Data Set)
An RRDS stores records in numbered slots. Each record is identified by its Relative Record Number (RRN) -- a positive integer starting at 1. Slots can be empty, allowing records to be inserted at specific positions and deleted without affecting other records.
RRDS is used when records have a natural numeric identifier that can serve as a slot number, such as: - Table entries indexed by a sequential number - Lookup tables where the key is a small numeric code - Slot-based scheduling systems
IDCAMS definition for an RRDS:
//DEFRRDS EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE CLUSTER ( -
NAME(PROD.BANKING.BRANCH.TABLE) -
NUMBERED -
RECORDS(10000 2000) -
RECORDSIZE(150 150) -
SHAREOPTIONS(2 3) -
) -
DATA ( -
NAME(PROD.BANKING.BRANCH.TABLE.DATA)-
CONTROLINTERVALSIZE(4096) -
)
/*
The keyword NUMBERED specifies an RRDS.
COBOL for an RRDS:
FILE-CONTROL.
SELECT BRANCH-TABLE
ASSIGN TO BRCHTBL
ORGANIZATION IS RELATIVE
ACCESS MODE IS RANDOM
RELATIVE KEY IS WS-BRANCH-SLOT
FILE STATUS IS WS-BRANCH-STATUS.
WORKING-STORAGE SECTION.
01 WS-BRANCH-SLOT PIC 9(04) COMP.
FILE SECTION.
FD BRANCH-TABLE
RECORD CONTAINS 150 CHARACTERS.
01 BRANCH-RECORD.
05 BR-BRANCH-CODE PIC X(04).
05 BR-BRANCH-NAME PIC X(30).
05 BR-BRANCH-ADDRESS PIC X(60).
05 BR-BRANCH-CITY PIC X(20).
05 BR-BRANCH-STATE PIC X(02).
05 BR-BRANCH-ZIP PIC X(10).
05 BR-MANAGER-ID PIC X(08).
05 FILLER PIC X(16).
The RELATIVE KEY IS WS-BRANCH-SLOT clause specifies a working-storage variable that holds the slot number for random access operations. Note that the relative key must be defined in working storage, not in the file section.
LDS (Linear Data Set)
A Linear Data Set stores data as a continuous byte stream without any record structure. LDS is not accessed through standard COBOL file I/O. Instead, it is used by subsystems such as DB2 (for tablespaces and indexes), CICS (for shared data tables), and application programs that use Data-in-Virtual (DIV) services to map the dataset directly into virtual storage.
COBOL programmers do not typically code SELECT/FD entries for an LDS, but it is important to recognize this type when you encounter it in a production environment. If your COBOL program accesses DB2, the underlying tablespace data resides in LDS datasets.
Dataset Naming Conventions
z/OS dataset names follow strict rules that enforce organization and security:
Basic Rules
- A dataset name consists of one or more qualifiers separated by periods
- Each qualifier can be 1 to 8 characters long
- The total dataset name can be up to 44 characters including the periods
- Each qualifier must begin with a letter (A-Z) or a national character (#, $, @)
- Remaining characters can be letters, digits (0-9), national characters, or hyphens
- Names are not case-sensitive (stored in uppercase)
High-Level Qualifier (HLQ)
The first qualifier is the High-Level Qualifier (HLQ). The HLQ is critically important because it determines:
- Catalog location -- the HLQ maps to a user catalog through the master catalog
- Security -- RACF rules are typically based on HLQ patterns
- Storage management -- SMS ACS routines use the HLQ to assign storage classes
In most installations, the HLQ follows a convention:
PROD.BANKING.ACCT.MASTER Production banking account master
TEST.BANKING.ACCT.MASTER Test copy of the same dataset
DEV.JSMITH.BANKING.ACCT.MASTER Developer's personal copy
SYS1.PARMLIB System parameter library
Common HLQ conventions:
| HLQ Pattern | Usage |
|---|---|
PROD |
Production datasets |
TEST |
System test datasets |
QA |
Quality assurance datasets |
DEV |
Development datasets |
SYS1 |
z/OS system datasets |
userid |
Personal datasets (e.g., TSO user ID) |
Naming Convention Example (Financial Institution)
A well-designed naming convention makes datasets self-documenting:
HLQ.application.type.description
PROD.BANKING.ACCTMAST.DATA Account master - data component
PROD.BANKING.ACCTMAST.INDEX Account master - index component
PROD.BANKING.TRANS.DAILY Daily transaction input
PROD.BANKING.TRANS.DAILY.LOG Transaction processing log
PROD.BANKING.REPORT.STMTS Customer statement report
PROD.BANKING.EXTRACT.FEDWIRE FedWire extract file
PROD.BANKING.GDG.TRANS.BACKUP GDG base for transaction backups
Generation Data Groups (GDG) -- Naming
A Generation Data Group is a collection of historically related datasets, called generations, that are managed as a unit. The GDG base name follows standard naming rules, and individual generations are identified by a generation and version number appended by the system:
PROD.BANKING.TRANS.BACKUP <-- GDG base (catalog entry only)
PROD.BANKING.TRANS.BACKUP.G0001V00 <-- Generation 1
PROD.BANKING.TRANS.BACKUP.G0002V00 <-- Generation 2
PROD.BANKING.TRANS.BACKUP.G0003V00 <-- Generation 3 (most recent)
GDG naming is covered in detail in the Generation Data Groups section later in this chapter.
Record Formats
Every non-VSAM dataset on z/OS has a record format (RECFM) that defines how records are structured within physical blocks on disk. Understanding record formats is essential for COBOL programmers because mismatches between the COBOL program's expectations and the dataset's actual record format cause abends.
Record Format Types
+------------------------------------------------------------------+
| Fixed-Length Records (RECFM=FB) |
| |
| Block: |
| +--------+--------+--------+--------+--------+ |
| | 200 B | 200 B | 200 B | 200 B | 200 B | |
| +--------+--------+--------+--------+--------+ |
| |<------------- BLKSIZE = 1000 bytes -------->| |
| |
| Every record is exactly LRECL bytes. |
| Every full block contains BLKSIZE/LRECL records. |
+------------------------------------------------------------------+
+------------------------------------------------------------------+
| Variable-Length Records (RECFM=VB) |
| |
| Block: |
| +-----+----+--------+----+-----------+----+------+ |
| | BDW |RDW | Data |RDW | Data |RDW | Data | |
| | 4B | 4B | varies | 4B | varies | 4B | var | |
| +-----+----+--------+----+-----------+----+------+ |
| |<------------- up to BLKSIZE bytes ----------->| |
| |
| BDW = Block Descriptor Word (4 bytes) |
| RDW = Record Descriptor Word (4 bytes, contains record length) |
| LRECL = maximum record length (including 4-byte RDW) |
+------------------------------------------------------------------+
| RECFM | Description | Usage |
|---|---|---|
| F | Fixed-length, unblocked | One record per block (inefficient, rarely used) |
| FB | Fixed-length, blocked | Standard for most batch files; multiple records per block |
| FBA | Fixed-length, blocked, ASA control | Print files with ASA carriage control in column 1 |
| FBM | Fixed-length, blocked, machine control | Print files with machine carriage control |
| V | Variable-length, unblocked | One record per block (rarely used) |
| VB | Variable-length, blocked | Standard for variable-length records |
| VBA | Variable-length, blocked, ASA | Print files with variable records and ASA control |
| VBS | Variable-length, blocked, spanned | Records can span blocks (for very large records) |
| U | Undefined | Load modules; no record structure imposed by the system |
LRECL (Logical Record Length)
For fixed-length records (F, FB, FBA), LRECL is the exact length of every record in the dataset.
For variable-length records (V, VB, VBA), LRECL is the maximum record length, which includes the 4-byte Record Descriptor Word (RDW). So if your longest data record is 496 bytes, LRECL would be 500 (496 + 4 for the RDW).
BLKSIZE (Block Size)
The block size determines how many logical records are grouped into a single physical block for I/O. Larger blocks mean fewer I/O operations and more efficient use of disk space, because each block has a small inter-block gap on disk.
For fixed-length records: BLKSIZE must be an exact multiple of LRECL.
BLKSIZE calculation for FB records:
LRECL = 200
Blocking factor of 5: BLKSIZE = 200 x 5 = 1,000
Blocking factor of 10: BLKSIZE = 200 x 10 = 2,000
Blocking factor of 50: BLKSIZE = 200 x 50 = 10,000
For variable-length records: BLKSIZE must be at least LRECL + 4 (to accommodate the 4-byte Block Descriptor Word plus at least one full record).
BLKSIZE calculation for VB records:
LRECL = 500 (max record length including RDW)
Minimum BLKSIZE = 500 + 4 (BDW) = 504
Practical BLKSIZE = 27998 (allows many records per block)
The recommended practice on z/OS is to specify BLKSIZE=0 (or omit it entirely and code BLOCK CONTAINS 0 RECORDS in COBOL). When BLKSIZE is zero, the system calculates the optimal block size based on the device type, maximizing the amount of data per track. This is called System-Determined Blocksize (SDB) and is the standard on modern z/OS systems.
//OUTPUT DD DSN=PROD.BANKING.REPORT.DAILY,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(2,1),RLSE),
// DCB=(RECFM=FBA,LRECL=133,BLKSIZE=0)
ASA Carriage Control Characters
When RECFM includes A (ASA control), the first byte of each record is a carriage control character that controls printer spacing:
| Character | Action |
|---|---|
(space) |
Single space before printing (normal) |
0 |
Double space before printing (skip a line) |
- |
Triple space before printing |
+ |
No space (overprint on same line) |
1 |
Skip to top of next page |
In COBOL, when writing to a print file with ASA control, the WRITE ... AFTER ADVANCING statement generates these control characters:
FD DAILY-REPORT
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 133 CHARACTERS
LABEL RECORDS ARE STANDARD.
01 REPORT-LINE PIC X(133).
PROCEDURE DIVISION.
WRITE REPORT-LINE FROM WS-HEADER-LINE
AFTER ADVANCING PAGE.
WRITE REPORT-LINE FROM WS-DETAIL-LINE
AFTER ADVANCING 1 LINE.
WRITE REPORT-LINE FROM WS-SUBTOTAL-LINE
AFTER ADVANCING 2 LINES.
The AFTER ADVANCING PAGE generates a 1 in the ASA control position. AFTER ADVANCING 1 LINE generates a space. AFTER ADVANCING 2 LINES generates a 0.
Space Allocation
When creating a new non-VSAM dataset, you must specify how much disk space to allocate. This is done through the SPACE parameter on the JCL DD statement.
Allocation Units
Space can be requested in three units:
| Unit | Keyword | Description |
|---|---|---|
| Tracks | TRK |
Individual tracks on a DASD volume (smallest unit) |
| Cylinders | CYL |
Full cylinders (one cylinder = 15 tracks on a 3390 device) |
| Blocks | nnnnn |
Average block size in bytes; system converts to tracks |
On a 3390 DASD device (the standard modern mainframe disk): - One track holds 56,664 bytes - One cylinder = 15 tracks = 849,960 bytes (approximately 850 KB)
SPACE Parameter Syntax
SPACE=(unit,(primary,secondary,directory),RLSE)
- unit -- TRK, CYL, or block size in bytes
- primary -- initial space allocation (required)
- secondary -- additional space allocated when primary fills up (up to 15 secondary extents per volume)
- directory -- number of 256-byte directory blocks (for PDS only)
- RLSE -- release unused space when the dataset is closed
Allocation Examples
Small transaction file (fixed-length, 200-byte records, ~50,000 records):
//TRANSOUT DD DSN=PROD.BANKING.TRANS.EXTRACT,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(2,1),RLSE),
// DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
Calculation: 50,000 records x 200 bytes = 10,000,000 bytes. 10,000,000 / 849,960 = approximately 12 tracks, which is less than 1 cylinder. Allocating 2 cylinders as primary provides comfortable headroom.
Large account master file (350-byte records, ~500,000 records):
//ACCTMAST DD DSN=PROD.BANKING.ACCT.MASTER.BACKUP,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(50,10),RLSE),
// DCB=(RECFM=FB,LRECL=350,BLKSIZE=0)
Calculation: 500,000 x 350 = 175,000,000 bytes. 175,000,000 / 849,960 = approximately 206 tracks = ~14 cylinders. Allocating 50 cylinders provides room for growth.
PDS for COBOL source (80-byte records, 50 members):
//SRCLIB DD DSN=DEV.BANKING.COBOL.SOURCE,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(5,2,50)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// DSNTYPE=LIBRARY
The 50 in the SPACE parameter allocates 50 directory blocks for a PDS. Each directory block holds approximately 6 entries (for names only) or fewer entries if user data (such as ISPF statistics) is stored.
Space Calculation Guidelines
A practical approach to space calculation:
Step 1: Estimate the data size
Number of records x LRECL = Total bytes
Step 2: Convert to tracks
Total bytes / 56,664 = Tracks (for 3390)
Step 3: Convert to cylinders
Tracks / 15 = Cylinders
Step 4: Add headroom
Multiply by 1.2 to 1.5 for growth
Step 5: Set secondary
Secondary = 20-50% of primary
When in doubt, over-allocate slightly and use RLSE to release unused space. Running out of space (abend SB37 or E37) during a critical production batch run is far more costly than wasting a few extra tracks of disk.
DASD Concepts
Volumes
A volume is a physical or logical disk unit identified by a six-character volume serial number (volser). Examples: PROD01, TST001, SYS001.
+------------------------------------------------------------------+
| DASD Volume (e.g., volser PROD01) |
| |
| +--VTOC--+ +---------+ +---------+ +---------+ +-------+ |
| | Volume | | Dataset | | Dataset | | Dataset | | Free | |
| | Table | | #1 | | #2 | | #3 | | Space | |
| | of | | (Extents| | (Extents| | (Extents| | | |
| | Content| | on vol)| | on vol)| | on vol)| | | |
| +--------+ +---------+ +---------+ +---------+ +-------+ |
| |
| Each volume has its own VTOC describing datasets on that volume |
+------------------------------------------------------------------+
On modern z/OS systems with SMS (Storage Management Subsystem), the programmer rarely needs to specify a volume. SMS automatically selects an appropriate volume based on storage policies. However, you may encounter the VOL=SER= parameter in older JCL:
//OLDFILE DD DSN=ARCHIVE.BANKING.TRANS.2024,
// DISP=SHR,
// VOL=SER=ARC001
VTOC (Volume Table of Contents)
Every DASD volume has a VTOC -- a special area that describes all datasets residing on that volume. The VTOC contains:
- The volume label (volser)
- A list of all datasets on the volume
- The location (extent information) of each dataset
- Free space information
When a dataset is allocated, an entry is written to the VTOC of the volume where the dataset resides. When the dataset is deleted, the VTOC entry is removed and the space is returned to the free space pool.
Catalogs
While the VTOC tells you what datasets are on a specific volume, catalogs provide the system-wide directory that maps dataset names to volumes. Without a catalog, you would need to know which volume a dataset is on before you could access it. With cataloging, you only need the dataset name -- the catalog tells the system where to find it.
+------------------------------------------------------------------+
| Catalog Structure |
| |
| +--MASTER CATALOG--+ |
| | (one per system) | |
| | | |
| | SYS1 --> VOL001 | (system datasets cataloged directly) |
| | PROD --> User Cat| (alias points to user catalog) |
| | TEST --> User Cat| (alias points to user catalog) |
| | DEV --> User Cat| (alias points to user catalog) |
| +------------------+ |
| | |
| v |
| +--USER CATALOG (PROD)--+ +--USER CATALOG (TEST)--+ |
| | | | | |
| | PROD.BANKING.ACCT.MAST | | TEST.BANKING.ACCT.MAST | |
| | --> VOL=PROD01 | | --> VOL=TST001 | |
| | PROD.BANKING.TRANS.LOG | | TEST.BANKING.TRANS.LOG | |
| | --> VOL=PROD02 | | --> VOL=TST001 | |
| +------------------------+ +------------------------+ |
+------------------------------------------------------------------+
z/OS uses a two-level catalog structure:
-
Master catalog -- one per z/OS system. Contains entries for system datasets (SYS1., etc.) and aliases* that point to user catalogs.
-
User catalogs -- contain the actual dataset-to-volume mappings for application datasets. Each HLQ (or group of HLQs) is associated with a specific user catalog through an alias in the master catalog.
When you code DISP=(NEW,CATLG,DELETE) in JCL, you are telling the system to create an entry in the appropriate catalog when the job step completes successfully, or to delete the dataset if the step abends.
DISP Parameter and Catalog Operations
The DISP parameter controls how the system handles dataset existence, sharing, and end-of-step disposition:
DISP=(status,normal-disposition,abnormal-disposition)
| Status | Meaning |
|---|---|
NEW |
Dataset does not exist; create it |
OLD |
Dataset exists; request exclusive access |
SHR |
Dataset exists; allow shared access |
MOD |
If exists, open for extend (append); if not, create |
| Disposition | Meaning |
|---|---|
KEEP |
Retain the dataset (do not delete) |
DELETE |
Delete the dataset |
CATLG |
Catalog the dataset (and keep it) |
UNCATLG |
Remove the catalog entry (keep the dataset on the volume) |
PASS |
Pass the dataset to a subsequent step in the same job |
Common DISP combinations:
DISP=(NEW,CATLG,DELETE) Create new, catalog if OK, delete if abend
DISP=SHR Existing, shared (read-only typical)
DISP=OLD Existing, exclusive (for update)
DISP=(MOD,CATLG) Append to existing or create new, catalog
DISP=(OLD,DELETE) Open existing exclusively, delete when done
DISP=(NEW,PASS) Create new, pass to next step (temporary)
SMS (Storage Management Subsystem)
The Storage Management Subsystem (SMS) is the z/OS facility that automates the management of dataset storage. Before SMS, programmers had to specify volumes, device types, and manage space manually. With SMS, storage policies are defined by the storage administrator, and datasets are automatically assigned to appropriate storage based on rules.
SMS Constructs
SMS uses three types of constructs (policy objects) to manage datasets:
+------------------------------------------------------------------+
| SMS Constructs |
| |
| +-- STORAGE CLASS --+ Defines performance characteristics |
| | - I/O priority | Examples: STANDARD, HIGHPERF, ARCHIVE |
| | - Response time | |
| | - Availability | |
| +-------------------+ |
| |
| +-- MANAGEMENT CLASS -+ Defines lifecycle/migration rules |
| | - Days since use | Examples: PROD30, ARCHIVE365 |
| | - Migration policy | |
| | - Backup frequency | |
| | - Retention period | |
| +---------------------+ |
| |
| +-- DATA CLASS -------+ Defines dataset attributes |
| | - RECFM | Examples: FB80, FB200, VB4096 |
| | - LRECL | |
| | - Space | |
| | - DSNTYPE | |
| +---------------------+ |
+------------------------------------------------------------------+
Storage Class -- defines where and how data is stored from a performance perspective. A storage class named HIGHPERF might direct data to the fastest DASD volumes; STANDARD would use normal volumes; ARCHIVE might allow migration to tape or lower-tier storage.
Management Class -- defines the lifecycle of a dataset. It controls how long a dataset is retained, when it is eligible for migration to cheaper storage, how often it is backed up, and whether it has an expiration date.
Data Class -- defines the default physical attributes of a dataset (RECFM, LRECL, BLKSIZE, SPACE, and DSNTYPE). If a data class is assigned to a dataset, you can omit these attributes from the JCL, and the data class defaults will be used.
ACS Routines
SMS assignment is controlled by Automatic Class Selection (ACS) routines -- installation-written programs that examine dataset characteristics (name, owner, type, etc.) and assign the appropriate SMS classes. For example, an ACS routine might implement rules like:
IF dataset name begins with 'PROD.BANKING'
THEN Storage Class = HIGHPERF
Management Class = PROD30
Data Class = (derived from dataset type)
IF dataset name begins with 'DEV'
THEN Storage Class = STANDARD
Management Class = DEV7
Data Class = (derived from dataset type)
SMS-Managed vs. Non-SMS Datasets
On a modern z/OS system, nearly all datasets are SMS-managed. An SMS-managed dataset has at least a storage class assigned to it. You can explicitly request SMS classes in JCL:
//NEWFILE DD DSN=PROD.BANKING.REPORT.MONTHLY,
// DISP=(NEW,CATLG,DELETE),
// STORCLAS=HIGHPERF,
// MGMTCLAS=PROD30,
// DATACLAS=FB133,
// SPACE=(CYL,(5,2),RLSE)
However, in practice, the ACS routines usually assign classes automatically based on the dataset name, and the programmer does not need to code these parameters. The storage administrator manages the policies centrally, which is one of the key benefits of SMS.
Generation Data Groups (GDG)
A Generation Data Group (GDG) is a collection of chronologically related datasets grouped under a single catalog entry called the GDG base. Each individual dataset in the group is called a generation. GDGs are one of the most important and heavily used dataset management features in z/OS batch processing.
Why GDGs Exist
Consider a daily batch process that creates a backup of the transaction file. Without GDGs, you would need to manage individual dataset names:
PROD.BANKING.TRANS.BACKUP.D20260201
PROD.BANKING.TRANS.BACKUP.D20260202
PROD.BANKING.TRANS.BACKUP.D20260203
...
This requires the JCL to generate a unique name each day, and you must write your own logic to manage retention (how many days to keep) and cleanup. GDGs solve this problem by providing built-in generation management, relative referencing, and automatic cleanup.
Creating a GDG Base
A GDG base is defined using IDCAMS. It is a catalog entry only -- no physical space is allocated:
//DEFGDG EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE GDG ( -
NAME(PROD.BANKING.TRANS.BACKUP) -
LIMIT(30) -
NOEMPTY -
SCRATCH -
)
/*
Key parameters:
NAME-- the GDG base nameLIMIT(30)-- maximum number of generations to retain. When the 31st generation is created, the oldest is automatically uncataloged (and optionally deleted)NOEMPTY-- when the limit is exceeded, only the oldest generation is removed. The alternative,EMPTY, removes ALL existing generations when the limit is exceeded (rarely desired)SCRATCH-- when a generation is uncataloged (rolled off), also delete it from the volume. The alternative,NOSCRATCH, uncatalogs but leaves the data on disk
Absolute vs. Relative Generation References
Each generation has an absolute name assigned by the system:
PROD.BANKING.TRANS.BACKUP.G0001V00 (generation 1)
PROD.BANKING.TRANS.BACKUP.G0002V00 (generation 2)
PROD.BANKING.TRANS.BACKUP.G0003V00 (generation 3)
But in JCL, you almost always use relative references:
| Reference | Meaning |
|---|---|
(0) |
Current (most recent) generation |
(+1) |
Next generation (new -- to be created) |
(-1) |
Previous generation (one before current) |
(-2) |
Two generations before current |
+------------------------------------------------------------------+
| GDG Relative References |
| |
| Existing generations: |
| G0028V00 = (-2) Two back |
| G0029V00 = (-1) Previous |
| G0030V00 = ( 0) Current (most recent) |
| ???????? = (+1) Next (will become G0031V00 when created) |
| |
| When (+1) is created and the job completes: |
| G0028V00 becomes (-3) |
| G0029V00 becomes (-2) |
| G0030V00 becomes (-1) |
| G0031V00 becomes ( 0) <-- new current |
+------------------------------------------------------------------+
GDG Usage in JCL
Creating a new generation:
//BACKUP DD DSN=PROD.BANKING.TRANS.BACKUP(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(10,5),RLSE),
// DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
The (+1) reference tells the system to create the next generation. The system assigns the absolute name (e.g., G0031V00) automatically.
Reading the current generation:
//INPUT DD DSN=PROD.BANKING.TRANS.BACKUP(0),DISP=SHR
Reading the previous generation:
//PREV DD DSN=PROD.BANKING.TRANS.BACKUP(-1),DISP=SHR
GDG in a Complete Batch Job
Here is a realistic example showing a daily batch job that reads today's transactions, processes them against the account master, creates a backup of the transactions, and produces a report:
//DAILYRUN JOB (ACCT001,'BANKING'),
// 'DAILY BATCH',
// CLASS=A,
// MSGCLASS=X,
// MSGLEVEL=(1,1),
// NOTIFY=&SYSUID
//*
//* STEP 1: PROCESS DAILY TRANSACTIONS
//*
//STEP01 EXEC PGM=BKTR1000
//STEPLIB DD DSN=PROD.BANKING.LOADLIB,DISP=SHR
//TRANSIN DD DSN=PROD.BANKING.TRANS.DAILY,DISP=SHR
//ACCTMAST DD DSN=PROD.BANKING.ACCT.MASTER,DISP=OLD
//TRANSOUT DD DSN=PROD.BANKING.TRANS.BACKUP(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(10,5),RLSE),
// DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
//REPORT DD DSN=PROD.BANKING.REPORT.DAILY(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(2,1),RLSE),
// DCB=(RECFM=FBA,LRECL=133,BLKSIZE=0)
//ERRFILE DD DSN=PROD.BANKING.TRANS.ERRORS(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,(5,5),RLSE),
// DCB=(RECFM=FB,LRECL=250,BLKSIZE=0)
//SYSOUT DD SYSOUT=*
//*
//* STEP 2: COMPARE TODAY'S BACKUP WITH YESTERDAY'S
//*
//STEP02 EXEC PGM=BKTR2000,
// COND=(4,LT,STEP01)
//STEPLIB DD DSN=PROD.BANKING.LOADLIB,DISP=SHR
//TODAY DD DSN=PROD.BANKING.TRANS.BACKUP(+1),DISP=SHR
//YESTERDY DD DSN=PROD.BANKING.TRANS.BACKUP(0),DISP=SHR
//COMPARE DD SYSOUT=*
Note that in Step 2, (+1) refers to the generation being created in Step 1 (it has not yet been cataloged with its absolute name), and (0) refers to what was the current generation before this job started. The relative references are resolved within the scope of the entire job.
GDG Model DSCB
When creating new GDG generations, you can define a model DSCB (Dataset Control Block) that provides default DCB attributes for all generations. This avoids repeating DCB parameters in every JCL DD statement:
//DEFMODEL EXEC PGM=IEFBR14
//MODEL DD DSN=PROD.BANKING.TRANS.BACKUP,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,0),
// DCB=(RECFM=FB,LRECL=200,BLKSIZE=0),
// VOL=SER=PROD01
This creates a zero-length dataset on a specific volume that serves as the template. Subsequent generations inherit these DCB attributes if they are not overridden in the DD statement. On SMS-managed systems, a data class can serve a similar purpose.
Dataset Allocation in JCL -- The DD Statement
The DD (Data Definition) statement is the JCL mechanism that connects a program's logical file references to physical datasets. As covered in Chapter 27, the DD statement is the most complex and parameter-rich JCL statement. This section focuses on the parameters most relevant to dataset management.
Essential DD Parameters
DSN (DSNAME) -- Dataset Name
Specifies the name of the dataset:
//INPUT DD DSN=PROD.BANKING.ACCT.MASTER,DISP=SHR
//MEMBER DD DSN=PROD.BANKING.CONTROL(DAILYPARM),DISP=SHR
//NEWGEN DD DSN=PROD.BANKING.TRANS.BACKUP(+1),
// DISP=(NEW,CATLG,DELETE)
For PDS/PDSE members, the member name is enclosed in parentheses. For GDG generations, the relative generation number is in parentheses.
DISP -- Disposition
Controls dataset status and end-of-step processing, as described in the DASD Concepts section above.
SPACE -- Space Allocation
Specifies the space to allocate for new datasets:
SPACE=(CYL,(primary,secondary),RLSE) Non-partitioned
SPACE=(CYL,(primary,secondary,directory)) PDS
SPACE=(TRK,(primary,secondary),RLSE) Track allocation
SPACE=(average-blksize,(primary,secondary)) Block allocation
DCB -- Data Control Block
Specifies the physical characteristics of the dataset:
DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
The DCB subparameters can come from three sources, with this merge order (highest priority first):
- The DD statement DCB parameter
- The dataset label (existing dataset's attributes stored in the VTOC)
- The COBOL program's FD entry (through the DCB open exit)
This merge order is important: if the JCL specifies LRECL=200 but the existing dataset was created with LRECL=150, the JCL value takes precedence, which may cause an S013 abend if the program expects 150-byte records.
Best practice: For existing datasets, do not code DCB parameters in JCL. Let the system use the attributes stored with the dataset. For new datasets, always code RECFM, LRECL, and BLKSIZE=0 explicitly.
VOL -- Volume
Specifies the volume(s) where the dataset resides or should be created:
VOL=SER=PROD01 Single volume
VOL=(,,,3,SER=(PRD01,PRD02)) Multi-volume, retain up to 3 volumes
On SMS-managed systems, omit the VOL parameter and let SMS select the volume.
UNIT -- Device Type
Specifies the type of device:
UNIT=SYSDA Any DASD device (most common)
UNIT=3390 Specific device type
UNIT=TAPE Tape device
Like VOL, UNIT is typically unnecessary on SMS-managed systems.
Special DD Statements
SYSOUT -- Output to JES Spool
SYSOUT directs output to the JES (Job Entry Subsystem) spool for printing or viewing:
//SYSPRINT DD SYSOUT=*
//REPORT DD SYSOUT=A
//AUDIT DD SYSOUT=X
SYSOUT=* uses the MSGCLASS from the JOB statement. SYSOUT=A directs to output class A (typically printed). SYSOUT=X directs to output class X (typically held for viewing).
In COBOL:
FILE-CONTROL.
SELECT PRINT-FILE
ASSIGN TO SYSPRINT.
FILE SECTION.
FD PRINT-FILE
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 133 CHARACTERS
LABEL RECORDS ARE OMITTED.
01 PRINT-LINE PIC X(133).
Note LABEL RECORDS ARE OMITTED for SYSOUT datasets, since spool files do not have standard labels. [IBM Enterprise COBOL] On modern compilers, LABEL RECORDS is treated as a comment and can be omitted, but many shops retain it by convention.
DUMMY -- Null File
DUMMY provides a null file. Reads return end-of-file immediately; writes are discarded:
//ERRFILE DD DUMMY,
// DCB=(RECFM=FB,LRECL=250,BLKSIZE=0)
This is useful for suppressing optional files during testing, or when a step conditionally produces output that is not needed in every run. The DCB attributes may still be required to satisfy the program's expectations.
Concatenated DD Statements
Multiple datasets can be concatenated (logically joined) under a single DD name. The program reads through them as if they were a single sequential file:
//TRANSIN DD DSN=PROD.BANKING.TRANS.BRANCH01,DISP=SHR
// DD DSN=PROD.BANKING.TRANS.BRANCH02,DISP=SHR
// DD DSN=PROD.BANKING.TRANS.BRANCH03,DISP=SHR
// DD DSN=PROD.BANKING.TRANS.BRANCH04,DISP=SHR
The second, third, and fourth DD statements have no DD name -- they are continuations of the first. The COBOL program opens TRANSIN and reads records from all four datasets in sequence, receiving an end-of-file only after the last dataset is exhausted.
Rules for concatenation:
- All datasets must be sequential (or PDS members)
- The dataset with the largest block size should be listed first (or code BLKSIZE= on the first DD to accommodate the largest)
- Record formats should be compatible
- Maximum 255 datasets in a concatenation
This is commonly used to combine transaction files from multiple branches or sources:
FILE-CONTROL.
SELECT TRANSACTION-INPUT
ASSIGN TO TRANSIN
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-TRANS-STATUS.
PROCEDURE DIVISION.
1000-MAIN.
OPEN INPUT TRANSACTION-INPUT.
PERFORM UNTIL WS-TRANS-EOF
READ TRANSACTION-INPUT
AT END SET WS-TRANS-EOF TO TRUE
NOT AT END PERFORM 2000-PROCESS-TRANSACTION
END-READ
END-PERFORM.
CLOSE TRANSACTION-INPUT.
STOP RUN.
The COBOL program is completely unaware of the concatenation. It sees a single logical file.
Temporary Datasets and Instream Data
Temporary Datasets
A temporary dataset exists only for the duration of the job. It is automatically deleted when the job ends. Temporary datasets are used for intermediate results passed between job steps.
There are two ways to define a temporary dataset:
Method 1: DSN with ampersand prefix
//STEP01 EXEC PGM=SORT
//SORTOUT DD DSN=&&SORTED,
// DISP=(NEW,PASS),
// SPACE=(CYL,(5,2)),
// DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
//*
//STEP02 EXEC PGM=BKTR1000
//INPUT DD DSN=&&SORTED,DISP=(OLD,DELETE)
The && prefix marks the dataset name as temporary. DISP=(NEW,PASS) in Step 1 creates the dataset and passes it to the next step. DISP=(OLD,DELETE) in Step 2 opens it exclusively and deletes it when Step 2 completes. Even without explicit DELETE, temporary datasets are deleted at job end.
Method 2: No DSN at all
//WORKFILE DD SPACE=(CYL,(3,1)),
// DCB=(RECFM=FB,LRECL=100,BLKSIZE=0)
When no DSN is coded, the system creates a temporary dataset with a system-generated name. This method is typically used for work files that are used and discarded within a single step.
Instream Data
Instream data allows you to embed data directly in the JCL stream. The data follows the DD statement and is delimited by /* or by a custom delimiter:
//CTLCARD DD *
REPORT-DATE=2026-02-10
BRANCH-RANGE=0001-0500
OUTPUT-FORMAT=PDF
MAX-ERRORS=100
/*
For COBOL, this is read as a sequential file:
FILE-CONTROL.
SELECT CONTROL-CARD-FILE
ASSIGN TO CTLCARD
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-CTL-STATUS.
FILE SECTION.
FD CONTROL-CARD-FILE
RECORDING MODE IS F
RECORD CONTAINS 80 CHARACTERS.
01 CONTROL-CARD-RECORD PIC X(80).
WORKING-STORAGE SECTION.
01 WS-CTL-STATUS PIC X(02).
01 WS-CTL-EOF PIC X(01) VALUE 'N'.
88 CTL-EOF VALUE 'Y'.
01 WS-REPORT-DATE PIC X(10).
01 WS-MAX-ERRORS PIC 9(04).
PROCEDURE DIVISION.
1000-MAIN.
OPEN INPUT CONTROL-CARD-FILE.
PERFORM UNTIL CTL-EOF
READ CONTROL-CARD-FILE
AT END SET CTL-EOF TO TRUE
NOT AT END PERFORM 1100-PARSE-CONTROL
END-READ
END-PERFORM.
CLOSE CONTROL-CARD-FILE.
*> Continue with main processing...
1100-PARSE-CONTROL.
EVALUATE TRUE
WHEN CONTROL-CARD-RECORD(1:12)
= 'REPORT-DATE='
MOVE CONTROL-CARD-RECORD(13:10)
TO WS-REPORT-DATE
WHEN CONTROL-CARD-RECORD(1:11)
= 'MAX-ERRORS='
MOVE CONTROL-CARD-RECORD(12:4)
TO WS-MAX-ERRORS
END-EVALUATE.
Instream data records are always 80 bytes (the original punched card width), with RECFM=F and LRECL=80.
Using DD DATA for JCL-like content:
If your instream data contains // in columns 1-2 (which would otherwise be interpreted as a JCL statement) or /* (which would be interpreted as a delimiter), use DD DATA with a custom delimiter:
//SYSIN DD DATA,DLG=$$
// This line starts with // but won't be treated as JCL
/* This line won't end the data stream prematurely
$$
COBOL Program Interaction with Datasets
The connection between a COBOL program and z/OS datasets flows through several layers: the COBOL SELECT...ASSIGN clause maps a logical file name to a DD name; the JCL DD statement maps the DD name to a physical dataset; and the z/OS access method (QSAM, BSAM, or VSAM) performs the actual I/O.
The SELECT...ASSIGN Clause
[IBM Enterprise COBOL] The ASSIGN clause in IBM Enterprise COBOL can take several forms:
SELECT file-name ASSIGN TO ddname
SELECT file-name ASSIGN TO ddname-literal
SELECT file-name ASSIGN TO DYNAMIC ws-variable
The simplest and most common form assigns to a DD name that matches the name coded in the JCL:
SELECT ACCOUNT-MASTER ASSIGN TO ACCTMAST.
This requires a JCL DD statement named ACCTMAST:
//ACCTMAST DD DSN=PROD.BANKING.ACCT.MASTER,DISP=SHR
Dynamic assignment allows the DD name to be determined at runtime:
FILE-CONTROL.
SELECT VARIABLE-FILE
ASSIGN TO DYNAMIC WS-DD-NAME.
WORKING-STORAGE SECTION.
01 WS-DD-NAME PIC X(08) VALUE 'ACCTMAST'.
The program can change WS-DD-NAME before opening the file to redirect to a different DD statement.
FD Entry -- Describing the Record
The FD (File Description) entry in the Data Division describes the logical structure of the records in the file. Key clauses:
FD ACCOUNT-MASTER
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 350 CHARACTERS
LABEL RECORDS ARE STANDARD
DATA RECORD IS ACCOUNT-MASTER-RECORD.
| Clause | Purpose |
|---|---|
RECORDING MODE IS F |
Fixed-length records (F or V) |
RECORDING MODE IS V |
Variable-length records |
BLOCK CONTAINS 0 RECORDS |
System-determined blocksize [IBM Enterprise COBOL] |
RECORD CONTAINS n CHARACTERS |
Logical record length |
LABEL RECORDS ARE STANDARD |
Standard labels (all DASD datasets) |
DATA RECORD IS |
Names the 01-level record description |
For variable-length records, the FD uses a different form:
FD TRANSACTION-FILE
RECORDING MODE IS V
BLOCK CONTAINS 0 RECORDS
RECORD IS VARYING IN SIZE
FROM 50 TO 496 CHARACTERS
DEPENDING ON WS-REC-LENGTH.
01 TRANSACTION-RECORD.
05 TR-RECORD-TYPE PIC X(02).
05 TR-ACCOUNT-NUMBER PIC X(10).
05 TR-COMMON-DATA PIC X(38).
05 TR-VARIABLE-DATA PIC X(446).
Complete Example: Sequential File Processing
Here is a complete example showing a COBOL program that reads a daily transaction file and writes an error report, with corresponding JCL:
JCL:
//BKTR3000 JOB (ACCT001,'BANKING'),
// 'TRANS VALIDATION',
// CLASS=A,MSGCLASS=X,
// MSGLEVEL=(1,1),NOTIFY=&SYSUID
//*
//STEP01 EXEC PGM=BKTR3000
//STEPLIB DD DSN=PROD.BANKING.LOADLIB,DISP=SHR
//TRANSIN DD DSN=PROD.BANKING.TRANS.DAILY,DISP=SHR
//VALRPT DD DSN=PROD.BANKING.REPORT.VALIDATE(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(1,1),RLSE),
// DCB=(RECFM=FBA,LRECL=133,BLKSIZE=0)
//ERROUT DD DSN=PROD.BANKING.TRANS.ERRORS(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(TRK,(10,5),RLSE),
// DCB=(RECFM=FB,LRECL=250,BLKSIZE=0)
//SYSOUT DD SYSOUT=*
COBOL:
IDENTIFICATION DIVISION.
PROGRAM-ID. BKTR3000.
*================================================================*
* TRANSACTION VALIDATION PROGRAM *
* Reads daily transactions, validates, writes error report. *
*================================================================*
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT TRANSACTION-INPUT
ASSIGN TO TRANSIN
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-TRANS-STATUS.
SELECT VALIDATION-REPORT
ASSIGN TO VALRPT
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-RPT-STATUS.
SELECT ERROR-OUTPUT
ASSIGN TO ERROUT
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-ERR-STATUS.
DATA DIVISION.
FILE SECTION.
FD TRANSACTION-INPUT
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 200 CHARACTERS.
01 TRANS-INPUT-RECORD.
05 TI-ACCOUNT-NUMBER PIC X(10).
05 TI-TRANS-TYPE PIC X(02).
05 TI-TRANS-DATE PIC 9(08).
05 TI-TRANS-AMOUNT PIC S9(13)V99 COMP-3.
05 TI-DESCRIPTION PIC X(40).
05 FILLER PIC X(132).
FD VALIDATION-REPORT
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 133 CHARACTERS.
01 REPORT-LINE PIC X(133).
FD ERROR-OUTPUT
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 250 CHARACTERS.
01 ERROR-OUTPUT-RECORD.
05 EO-ACCOUNT-NUMBER PIC X(10).
05 EO-TRANS-TYPE PIC X(02).
05 EO-TRANS-DATE PIC 9(08).
05 EO-TRANS-AMOUNT PIC S9(13)V99 COMP-3.
05 EO-ERROR-CODE PIC X(04).
05 EO-ERROR-MESSAGE PIC X(60).
05 EO-ORIGINAL-RECORD PIC X(158).
WORKING-STORAGE SECTION.
01 WS-TRANS-STATUS PIC X(02).
01 WS-RPT-STATUS PIC X(02).
01 WS-ERR-STATUS PIC X(02).
01 WS-FLAGS.
05 WS-TRANS-EOF PIC X(01) VALUE 'N'.
88 TRANS-EOF VALUE 'Y'.
01 WS-COUNTERS.
05 WS-READ-COUNT PIC 9(09) VALUE ZERO.
05 WS-VALID-COUNT PIC 9(09) VALUE ZERO.
05 WS-ERROR-COUNT PIC 9(09) VALUE ZERO.
01 WS-REPORT-HEADER.
05 FILLER PIC X(01) VALUE '1'.
05 FILLER PIC X(20) VALUE SPACES.
05 FILLER PIC X(40)
VALUE 'TRANSACTION VALIDATION REPORT'.
05 FILLER PIC X(72) VALUE SPACES.
01 WS-DETAIL-LINE.
05 FILLER PIC X(01) VALUE SPACE.
05 WD-ACCOUNT PIC X(10).
05 FILLER PIC X(02) VALUE SPACES.
05 WD-TRANS-TYPE PIC X(02).
05 FILLER PIC X(02) VALUE SPACES.
05 WD-AMOUNT PIC Z(13)9.99-.
05 FILLER PIC X(02) VALUE SPACES.
05 WD-ERROR-MSG PIC X(60).
05 FILLER PIC X(36) VALUE SPACES.
PROCEDURE DIVISION.
0000-MAIN.
PERFORM 1000-INITIALIZE.
PERFORM 2000-PROCESS
UNTIL TRANS-EOF.
PERFORM 3000-FINALIZE.
STOP RUN.
1000-INITIALIZE.
OPEN INPUT TRANSACTION-INPUT.
OPEN OUTPUT VALIDATION-REPORT.
OPEN OUTPUT ERROR-OUTPUT.
WRITE REPORT-LINE FROM WS-REPORT-HEADER.
PERFORM 9000-READ-TRANSACTION.
2000-PROCESS.
ADD 1 TO WS-READ-COUNT.
IF TI-ACCOUNT-NUMBER = SPACES
OR TI-TRANS-TYPE NOT = 'CR' AND 'DB'
OR TI-TRANS-AMOUNT = ZERO
ADD 1 TO WS-ERROR-COUNT
PERFORM 2100-WRITE-ERROR
ELSE
ADD 1 TO WS-VALID-COUNT
END-IF.
PERFORM 9000-READ-TRANSACTION.
2100-WRITE-ERROR.
INITIALIZE ERROR-OUTPUT-RECORD.
MOVE TI-ACCOUNT-NUMBER TO EO-ACCOUNT-NUMBER.
MOVE TI-TRANS-TYPE TO EO-TRANS-TYPE.
MOVE TI-TRANS-DATE TO EO-TRANS-DATE.
MOVE TI-TRANS-AMOUNT TO EO-TRANS-AMOUNT.
EVALUATE TRUE
WHEN TI-ACCOUNT-NUMBER = SPACES
MOVE 'E001' TO EO-ERROR-CODE
MOVE 'MISSING ACCOUNT NUMBER'
TO EO-ERROR-MESSAGE
WHEN TI-TRANS-TYPE NOT = 'CR' AND 'DB'
MOVE 'E002' TO EO-ERROR-CODE
MOVE 'INVALID TRANSACTION TYPE'
TO EO-ERROR-MESSAGE
WHEN TI-TRANS-AMOUNT = ZERO
MOVE 'E003' TO EO-ERROR-CODE
MOVE 'ZERO TRANSACTION AMOUNT'
TO EO-ERROR-MESSAGE
END-EVALUATE.
WRITE ERROR-OUTPUT-RECORD.
MOVE SPACES TO WS-DETAIL-LINE.
MOVE SPACE TO WS-DETAIL-LINE(1:1).
MOVE TI-ACCOUNT-NUMBER TO WD-ACCOUNT.
MOVE TI-TRANS-TYPE TO WD-TRANS-TYPE.
MOVE TI-TRANS-AMOUNT TO WD-AMOUNT.
MOVE EO-ERROR-MESSAGE TO WD-ERROR-MSG.
WRITE REPORT-LINE FROM WS-DETAIL-LINE.
3000-FINALIZE.
CLOSE TRANSACTION-INPUT.
CLOSE VALIDATION-REPORT.
CLOSE ERROR-OUTPUT.
DISPLAY 'BKTR3000 COMPLETE: '
'READ=' WS-READ-COUNT
' VALID=' WS-VALID-COUNT
' ERRORS=' WS-ERROR-COUNT.
9000-READ-TRANSACTION.
READ TRANSACTION-INPUT
AT END SET TRANS-EOF TO TRUE
END-READ.
Complete Example: KSDS Random Access
This example shows a COBOL program that reads a KSDS account master by key, with full JCL:
JCL:
//BKAC1000 JOB (ACCT001,'BANKING'),
// 'ACCOUNT INQUIRY',
// CLASS=A,MSGCLASS=X,
// MSGLEVEL=(1,1),NOTIFY=&SYSUID
//*
//STEP01 EXEC PGM=BKAC1000
//STEPLIB DD DSN=PROD.BANKING.LOADLIB,DISP=SHR
//ACCTMAST DD DSN=PROD.BANKING.ACCT.MASTER,DISP=SHR
//INQUIRY DD DSN=PROD.BANKING.INQUIRY.INPUT,DISP=SHR
//RPTOUT DD SYSOUT=*
COBOL:
IDENTIFICATION DIVISION.
PROGRAM-ID. BKAC1000.
*================================================================*
* ACCOUNT INQUIRY PROGRAM *
* Reads account numbers from inquiry file, retrieves account *
* details from KSDS master file, produces inquiry report. *
*================================================================*
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT ACCOUNT-MASTER
ASSIGN TO ACCTMAST
ORGANIZATION IS INDEXED
ACCESS MODE IS RANDOM
RECORD KEY IS AM-ACCOUNT-NUMBER
FILE STATUS IS WS-ACCT-STATUS.
SELECT INQUIRY-FILE
ASSIGN TO INQUIRY
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-INQ-STATUS.
SELECT REPORT-OUTPUT
ASSIGN TO RPTOUT
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-RPT-STATUS.
DATA DIVISION.
FILE SECTION.
FD ACCOUNT-MASTER
RECORD CONTAINS 350 CHARACTERS.
01 ACCOUNT-MASTER-RECORD.
05 AM-ACCOUNT-NUMBER PIC X(10).
05 AM-CUSTOMER-NAME PIC X(30).
05 AM-ACCOUNT-TYPE PIC X(02).
05 AM-OPEN-DATE PIC 9(08).
05 AM-CURRENT-BALANCE PIC S9(13)V99 COMP-3.
05 AM-AVAILABLE-BALANCE PIC S9(13)V99 COMP-3.
05 AM-INTEREST-RATE PIC 9(03)V9(05) COMP-3.
05 AM-LAST-ACTIVITY-DATE PIC 9(08).
05 AM-STATUS-CODE PIC X(01).
05 AM-BRANCH-CODE PIC X(04).
05 FILLER PIC X(275).
FD INQUIRY-FILE
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 80 CHARACTERS.
01 INQUIRY-RECORD.
05 INQ-ACCOUNT-NUMBER PIC X(10).
05 FILLER PIC X(70).
FD REPORT-OUTPUT
RECORDING MODE IS F
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 133 CHARACTERS.
01 REPORT-LINE PIC X(133).
WORKING-STORAGE SECTION.
01 WS-ACCT-STATUS PIC X(02).
01 WS-INQ-STATUS PIC X(02).
01 WS-RPT-STATUS PIC X(02).
01 WS-FLAGS.
05 WS-INQ-EOF PIC X(01) VALUE 'N'.
88 INQ-EOF VALUE 'Y'.
01 WS-COUNTERS.
05 WS-INQUIRY-COUNT PIC 9(07) VALUE ZERO.
05 WS-FOUND-COUNT PIC 9(07) VALUE ZERO.
05 WS-NOTFOUND-COUNT PIC 9(07) VALUE ZERO.
01 WS-DETAIL-LINE.
05 FILLER PIC X(01) VALUE SPACE.
05 WD-ACCOUNT PIC X(10).
05 FILLER PIC X(02) VALUE SPACES.
05 WD-NAME PIC X(30).
05 FILLER PIC X(02) VALUE SPACES.
05 WD-TYPE PIC X(02).
05 FILLER PIC X(02) VALUE SPACES.
05 WD-BALANCE PIC Z(13)9.99-.
05 FILLER PIC X(02) VALUE SPACES.
05 WD-STATUS PIC X(08).
05 FILLER PIC X(56) VALUE SPACES.
PROCEDURE DIVISION.
0000-MAIN.
PERFORM 1000-INITIALIZE.
PERFORM 2000-PROCESS-INQUIRY
UNTIL INQ-EOF.
PERFORM 3000-FINALIZE.
STOP RUN.
1000-INITIALIZE.
OPEN INPUT ACCOUNT-MASTER.
IF WS-ACCT-STATUS NOT = '00'
DISPLAY 'ERROR OPENING ACCOUNT MASTER: '
WS-ACCT-STATUS
MOVE 16 TO RETURN-CODE
STOP RUN
END-IF.
OPEN INPUT INQUIRY-FILE.
OPEN OUTPUT REPORT-OUTPUT.
PERFORM 9000-READ-INQUIRY.
2000-PROCESS-INQUIRY.
ADD 1 TO WS-INQUIRY-COUNT.
MOVE INQ-ACCOUNT-NUMBER TO AM-ACCOUNT-NUMBER.
READ ACCOUNT-MASTER
INVALID KEY
ADD 1 TO WS-NOTFOUND-COUNT
PERFORM 2100-REPORT-NOT-FOUND
NOT INVALID KEY
ADD 1 TO WS-FOUND-COUNT
PERFORM 2200-REPORT-FOUND
END-READ.
PERFORM 9000-READ-INQUIRY.
2100-REPORT-NOT-FOUND.
INITIALIZE WS-DETAIL-LINE.
MOVE SPACE TO WS-DETAIL-LINE(1:1).
MOVE INQ-ACCOUNT-NUMBER TO WD-ACCOUNT.
MOVE '*** ACCOUNT NOT FOUND ***' TO WD-NAME.
WRITE REPORT-LINE FROM WS-DETAIL-LINE.
2200-REPORT-FOUND.
INITIALIZE WS-DETAIL-LINE.
MOVE SPACE TO WS-DETAIL-LINE(1:1).
MOVE AM-ACCOUNT-NUMBER TO WD-ACCOUNT.
MOVE AM-CUSTOMER-NAME TO WD-NAME.
MOVE AM-ACCOUNT-TYPE TO WD-TYPE.
MOVE AM-CURRENT-BALANCE TO WD-BALANCE.
EVALUATE AM-STATUS-CODE
WHEN 'A' MOVE 'ACTIVE ' TO WD-STATUS
WHEN 'C' MOVE 'CLOSED ' TO WD-STATUS
WHEN 'F' MOVE 'FROZEN ' TO WD-STATUS
WHEN OTHER MOVE 'UNKNOWN ' TO WD-STATUS
END-EVALUATE.
WRITE REPORT-LINE FROM WS-DETAIL-LINE.
3000-FINALIZE.
CLOSE ACCOUNT-MASTER
INQUIRY-FILE
REPORT-OUTPUT.
DISPLAY 'BKAC1000 COMPLETE: '
'INQUIRIES=' WS-INQUIRY-COUNT
' FOUND=' WS-FOUND-COUNT
' NOT-FOUND=' WS-NOTFOUND-COUNT.
9000-READ-INQUIRY.
READ INQUIRY-FILE
AT END SET INQ-EOF TO TRUE
END-READ.
The key point: READ ACCOUNT-MASTER INVALID KEY performs a random read by the primary key (AM-ACCOUNT-NUMBER). Before the READ, the program moves the desired key value into the record key field. The VSAM access method uses the KSDS index to locate the record directly, without reading through the entire file.
File Status Codes
Every SELECT statement should include a FILE STATUS clause. After every I/O operation, the runtime sets the file status variable to a two-byte code indicating the result. Checking file status is essential for robust error handling:
| Status | Meaning |
|---|---|
00 |
Successful completion |
02 |
Successful, duplicate alternate key exists |
04 |
Record length does not match fixed-length file |
10 |
End of file reached |
22 |
Duplicate primary key on WRITE |
23 |
Record not found on READ (INVALID KEY) |
30 |
Permanent I/O error |
34 |
Boundary violation (out of space) |
35 |
File not found (dataset does not exist) |
37 |
File not opened in correct mode |
39 |
Conflict between COBOL FD and dataset attributes |
41 |
File already open |
42 |
File already closed |
46 |
Sequential READ when no valid next record |
47 |
READ attempted on file not opened for input/I-O |
48 |
WRITE attempted on file not opened for output/I-O/extend |
49 |
DELETE/REWRITE on file not opened for I-O |
92 |
Logic error (various causes) |
93 |
Resource unavailable (VSAM file in use) |
[IBM Enterprise COBOL] IBM Enterprise COBOL also provides an extended file status code through the VSAM-STATUS special register and through the two-byte extended status returned in positions 3-4 of a 4-byte status area when you define the status field as PIC X(04).
A robust file status checking routine:
WORKING-STORAGE SECTION.
01 WS-FILE-STATUS PIC X(02).
01 WS-FILE-NAME PIC X(30).
PROCEDURE DIVISION.
9100-CHECK-FILE-STATUS.
IF WS-FILE-STATUS NOT = '00' AND '10'
DISPLAY '*** FILE ERROR ***'
DISPLAY 'FILE: ' WS-FILE-NAME
DISPLAY 'STATUS: ' WS-FILE-STATUS
EVALUATE WS-FILE-STATUS
WHEN '22'
DISPLAY 'DUPLICATE KEY'
WHEN '23'
DISPLAY 'RECORD NOT FOUND'
WHEN '30'
DISPLAY 'PERMANENT I/O ERROR'
WHEN '34'
DISPLAY 'OUT OF SPACE (BOUNDARY)'
WHEN '35'
DISPLAY 'FILE NOT FOUND'
WHEN '39'
DISPLAY 'FD/DATASET ATTRIBUTE CONFLICT'
WHEN '93'
DISPLAY 'RESOURCE UNAVAILABLE'
WHEN OTHER
DISPLAY 'UNEXPECTED STATUS CODE'
END-EVALUATE
MOVE 16 TO RETURN-CODE
STOP RUN
END-IF.
Common Errors and Troubleshooting
Dataset-related errors are among the most frequent causes of batch job failures. Understanding these abends and their remedies is a critical skill for COBOL programmers.
System Abend Codes (Dataset-Related)
S013 -- Conflicting DCB Attributes
Cause: The record format, record length, or block size specified in the program (FD entry), the JCL (DCB parameter), or the dataset label do not agree.
Common scenarios:
- COBOL FD says RECORD CONTAINS 200 CHARACTERS but the dataset was created with LRECL=150
- JCL specifies DCB=(RECFM=FB,LRECL=200) but the existing dataset has LRECL=250
- Attempting to read a variable-length dataset with a fixed-length FD
Resolution: 1. Check the dataset attributes using ISPF option 3.4 or the LISTDS TSO command 2. Compare with the COBOL FD entry 3. Compare with the JCL DCB parameters 4. Correct the mismatch -- usually the COBOL program and JCL should match the dataset
//* Use IDCAMS LISTCAT to check VSAM dataset attributes:
//CHECK EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT ENT(PROD.BANKING.ACCT.MASTER) ALL
/*
S0C4 -- Protection Exception / Addressing Error
Cause (file-related): Reading past the end of a record because the FD record layout is larger than the actual record length, or accessing a file that was not properly opened.
Resolution:
1. Verify the COBOL 01-level record length matches LRECL
2. Ensure the file is opened before I/O operations
3. Check that the file status is 00 after OPEN
S213 -- Dataset Not Found on Volume
Cause: The system cannot find the specified dataset on the indicated volume, or the catalog entry points to a volume where the dataset no longer exists.
Resolution:
1. Verify the dataset name is spelled correctly in JCL
2. Check if the dataset is cataloged: use ISPF 3.4 or LISTCAT
3. If the dataset was recently migrated by HSM, it may need recall:
jcl
//RECALL EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
HRECALL 'PROD.BANKING.ACCT.MASTER'
/*
B37 -- Dataset Out of Space (End of Volume)
Cause: The dataset has exhausted all allocated space (primary + all 15 secondary extents) and there is no more room on the volume.
Resolution:
1. Increase the SPACE allocation (larger primary or secondary)
2. Specify RLSE to release unused space from other datasets on the same volume
3. Allocate to multiple volumes if necessary
4. For VSAM, increase the RECORDS or KILOBYTES allocation
5. Consider compressing or archiving data before processing
//* Increased allocation to prevent B37:
//OUTPUT DD DSN=PROD.BANKING.REPORT.DAILY(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(20,10),RLSE),
// DCB=(RECFM=FBA,LRECL=133,BLKSIZE=0)
E37 -- Dataset Out of Space (Extent Limit)
Cause: Similar to B37, but specifically means the maximum number of extents (typically 16 for non-VSAM) has been reached.
Resolution: Same as B37, but focus on making the primary allocation large enough to avoid excessive secondary extents.
S001 -- I/O Error
Cause: A physical or logical I/O error occurred. Common subcauses include reading past end-of-file, incorrect block size, or hardware errors.
Resolution: 1. Check for missing DD statements 2. Verify block sizes are correct 3. For concatenated datasets, ensure the first dataset has the largest BLKSIZE 4. Run IEBGENER to verify the dataset can be read
VSAM-Specific Errors
VSAM uses its own set of return and reason codes, reported through the COBOL file status:
| File Status | VSAM Feedback | Description |
|---|---|---|
| 00 | 00 | Successful |
| 02 | 00 | Duplicate alternate key |
| 22 | 08 | Duplicate primary key on WRITE |
| 23 | 10 | Record not found |
| 24 | 24 | Key sequence error (sequential WRITE) |
| 34 | 1C | Out of space |
| 35 | -- | File not found |
| 39 | -- | DCB conflict |
| 93 | -- | Resource unavailable (another job has exclusive access) |
The most common VSAM problems and solutions:
Problem: File status 93 -- Resource unavailable
This occurs when another job or CICS region has the VSAM file open with exclusive access (or with incompatible share options).
* Check for file-in-use condition
OPEN I-O ACCOUNT-MASTER.
IF WS-ACCT-STATUS = '93'
DISPLAY 'ACCOUNT MASTER IN USE BY ANOTHER JOB'
DISPLAY 'RETRY LATER OR CHECK SHAREOPTIONS'
MOVE 12 TO RETURN-CODE
STOP RUN
END-IF.
Resolution: 1. Check if another job is using the dataset (use SDSF or operator commands) 2. Verify SHAREOPTIONS on the VSAM cluster allow concurrent access 3. If CICS owns the file, ensure batch access is coordinated
Problem: File status 35 -- File not found
Resolution:
1. Verify the DD name in COBOL ASSIGN TO matches the JCL DD name exactly
2. Verify the dataset name in JCL is correct
3. Check if the dataset is cataloged
4. For VSAM files, verify the cluster was defined with IDCAMS
Problem: File status 39 -- Attribute conflict
This is the VSAM equivalent of the S013 abend. The COBOL program's ORGANIZATION, RECORD KEY, or record length conflicts with the VSAM cluster definition.
Resolution:
1. Use IDCAMS LISTCAT ALL to check the cluster attributes
2. Verify ORGANIZATION IS INDEXED matches a KSDS
3. Verify RECORD KEY position and length match KEYS in the cluster definition
4. Verify record lengths match RECORDSIZE
Diagnostic JCL for Dataset Problems
Here is a utility JCL template for diagnosing dataset issues:
//*----------------------------------------------------------*
//* DIAGNOSTIC JOB FOR DATASET TROUBLESHOOTING *
//*----------------------------------------------------------*
//DIAGJOB JOB (ACCT001,'BANKING'),'DIAGNOSTICS',
// CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//*
//* STEP 1: LIST CATALOG ENTRY FOR VSAM CLUSTER
//*
//STEP01 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT ENT(PROD.BANKING.ACCT.MASTER) ALL
/*
//*
//* STEP 2: LIST CATALOG ENTRY FOR NON-VSAM DATASET
//*
//STEP02 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT ENT(PROD.BANKING.TRANS.DAILY) ALL
/*
//*
//* STEP 3: VERIFY VSAM FILE CAN BE OPENED
//*
//STEP03 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//INPUT DD DSN=PROD.BANKING.ACCT.MASTER,DISP=SHR
//SYSIN DD *
VERIFY DS(PROD.BANKING.ACCT.MASTER)
/*
//*
//* STEP 4: PRINT FIRST 10 RECORDS OF A SEQUENTIAL FILE
//*
//STEP04 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//INPUT DD DSN=PROD.BANKING.TRANS.DAILY,DISP=SHR
//SYSIN DD *
PRINT INFILE(INPUT) COUNT(10) CHAR
/*
//*
//* STEP 5: LIST GDG GENERATIONS
//*
//STEP05 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT ENT(PROD.BANKING.TRANS.BACKUP) GDG ALL
/*
Practical Reference: Dataset Type Selection Guide
When designing a new COBOL application, use this decision guide to select the appropriate dataset organization:
+------------------------------------------------------------------+
| DATASET TYPE SELECTION GUIDE |
| |
| Is the data accessed by a unique key? |
| | |
| +-- YES --> Do you need both sequential and random access? |
| | | |
| | +-- YES --> VSAM KSDS (ORGANIZATION IS INDEXED, |
| | | ACCESS MODE IS DYNAMIC) |
| | | |
| | +-- NO, random only --> VSAM KSDS |
| | (ACCESS MODE IS RANDOM) |
| | |
| +-- NO --> Is the data accessed by a numeric slot/position? |
| | |
| +-- YES --> VSAM RRDS (ORGANIZATION IS RELATIVE) |
| | |
| +-- NO --> Is the data always read/written in order? |
| | |
| +-- YES --> PS (sequential) or VSAM ESDS |
| | |
| +-- Need append-only audit trail? |
| --> VSAM ESDS |
| Otherwise --> PS (simplest) |
+------------------------------------------------------------------+
Financial Application Dataset Map
A typical banking batch application might use the following datasets:
| Dataset | Organization | RECFM | LRECL | Purpose |
|---|---|---|---|---|
PROD.BANKING.ACCT.MASTER |
VSAM KSDS | N/A | 350 | Account master file |
PROD.BANKING.CUST.MASTER |
VSAM KSDS | N/A | 500 | Customer master file |
PROD.BANKING.TRANS.DAILY |
PS | FB | 200 | Daily transaction input |
PROD.BANKING.TRANS.BACKUP |
GDG (PS) | FB | 200 | Transaction backup generations |
PROD.BANKING.TRANS.LOG |
VSAM ESDS | N/A | 200 | Audit/transaction log |
PROD.BANKING.REPORT.STMTS |
GDG (PS) | FBA | 133 | Customer statement report |
PROD.BANKING.REPORT.RECON |
PS | FBA | 133 | Reconciliation report |
PROD.BANKING.BRANCH.TABLE |
VSAM RRDS | N/A | 150 | Branch lookup table |
PROD.BANKING.CONTROL |
PDSE | FB | 80 | Control parameters (members) |
PROD.BANKING.COBOL.SOURCE |
PDSE | FB | 80 | COBOL source library |
PROD.BANKING.COBOL.COPYLIB |
PDSE | FB | 80 | COBOL copybook library |
PROD.BANKING.LOADLIB |
PDSE | U | 0 | Load module library |
PROD.BANKING.EXTRACT.FEDWIRE |
PS | FB | 300 | FedWire extract for wire transfers |
Advanced Topics
Multi-Volume Datasets
A dataset can span multiple DASD volumes when it is too large for a single volume. This is specified in JCL:
//BIGFILE DD DSN=PROD.BANKING.HISTORY.ANNUAL,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(500,100),RLSE),
// DCB=(RECFM=FB,LRECL=350,BLKSIZE=0),
// VOL=(,,,5)
The VOL=(,,,5) specifies that the dataset can extend across up to 5 volumes. On SMS-managed systems, multi-volume datasets are handled automatically by the storage policies.
From a COBOL perspective, multi-volume datasets are transparent. The access method handles volume switching automatically. Your program does not need any special code.
VSAM Alternate Indexes
A KSDS can have one or more alternate indexes that provide additional access paths. For example, the account master file might have the account number as the primary key, but you also need to access accounts by customer name or by Social Security number.
Define an alternate index:
//DEFAIX EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE ALTERNATEINDEX ( -
NAME(PROD.BANKING.ACCT.MASTER.AIX1) -
RELATE(PROD.BANKING.ACCT.MASTER) -
KEYS(9 42) -
UNIQUEKEY -
RECORDS(500000 100000) -
SHAREOPTIONS(2 3) -
)
DEFINE PATH ( -
NAME(PROD.BANKING.ACCT.MASTER.PATH1)-
PATHENTRY(PROD.BANKING.ACCT.MASTER.AIX1) -
)
BLDINDEX INDATASET(PROD.BANKING.ACCT.MASTER) -
OUTDATASET(PROD.BANKING.ACCT.MASTER.AIX1)
/*
This defines an alternate index on a 9-byte field starting at offset 42 (for example, a Social Security number). The PATH provides a named access point. BLDINDEX builds the index from the existing data.
COBOL access through an alternate index:
FILE-CONTROL.
SELECT ACCOUNT-BY-SSN
ASSIGN TO ACCTSSN
ORGANIZATION IS INDEXED
ACCESS MODE IS RANDOM
RECORD KEY IS AM-ACCOUNT-NUMBER
ALTERNATE RECORD KEY IS AM-SSN
FILE STATUS IS WS-SSN-STATUS.
//ACCTSSN DD DSN=PROD.BANKING.ACCT.MASTER.PATH1,DISP=SHR
When the file is opened through the PATH, reads using AM-SSN as the key use the alternate index to locate records.
Extended Format Datasets
[IBM Enterprise COBOL] Extended format datasets provide enhanced capabilities for large datasets:
- Extended sequential (DSNTYPE=LARGE) -- allows sequential datasets larger than 65,535 tracks per volume
- Extended format VSAM -- supports striping (data distributed across multiple volumes for parallel I/O) and data compression
These are enabled through SMS data class definitions and are transparent to COBOL programs. The performance improvements can be significant for very large datasets.
PDSE Program Objects
[IBM Enterprise COBOL] When a COBOL program is compiled and linked on a modern z/OS system, the load module is stored as a program object in a PDSE load library (DSNTYPE=LIBRARY). Program objects support features that traditional PDS load modules do not:
- Individual members larger than 16 MB
- No need for reblocking
- Automatic space reclamation
- Support for 64-bit addressing mode
The COBOL programmer does not need to do anything special -- the compiler and linkage editor handle this automatically when the target library is a PDSE.
Summary
z/OS datasets are the foundation of all data storage on the mainframe. As a COBOL programmer, your daily work involves creating, reading, writing, and managing datasets through JCL DD statements and COBOL file I/O. The key concepts covered in this chapter are:
Dataset organizations determine how data is physically and logically structured. Physical Sequential (PS) is the simplest and most common for batch input/output. Partitioned (PDS/PDSE) organizes related members under a single directory. VSAM provides ESDS for chronological logs, KSDS for keyed master files, and RRDS for slot-based access.
Naming conventions use a hierarchical qualifier structure. The High-Level Qualifier controls catalog routing, security, and SMS policy assignment. Disciplined naming makes datasets self-documenting and manageable.
Record formats (FB, VB, FBA, VBA, U) define the physical structure of records within blocks. LRECL and BLKSIZE must be consistent between the COBOL program, JCL, and the dataset itself. Use BLKSIZE=0 for system-determined optimal blocking.
Space allocation uses tracks, cylinders, or blocks as units. Primary and secondary quantities, combined with the RLSE option, provide flexible space management. Over-allocating with RLSE is safer than under-allocating.
DASD architecture -- volumes, VTOCs, and the two-level catalog structure (master catalog and user catalogs) -- provides the infrastructure that maps dataset names to physical locations.
SMS automates storage management through storage classes, management classes, and data classes, allowing the storage administrator to define policies that are applied automatically through ACS routines.
Generation Data Groups provide lifecycle management for chronologically related datasets. Relative references (+1, 0, -1) simplify JCL coding, and the LIMIT parameter automates retention management.
The COBOL-to-dataset connection flows from SELECT...ASSIGN through JCL DD statements to physical datasets. The FD entry describes record structure. File status codes provide essential feedback for error handling.
Understanding these concepts thoroughly will help you design efficient batch processes, diagnose dataset-related abends, and work effectively in the z/OS environment that hosts the world's most critical financial and business applications.
Key Terms
| Term | Definition |
|---|---|
| BLKSIZE | Block size -- the number of bytes in a physical block |
| Catalog | System directory mapping dataset names to volumes |
| CI (Control Interval) | Unit of I/O for VSAM datasets |
| DASD | Direct Access Storage Device -- disk storage |
| DCB | Data Control Block -- dataset physical attributes |
| DD statement | JCL statement defining a dataset for a program step |
| DISP | Disposition -- controls dataset status and end-of-step handling |
| ESDS | Entry-Sequenced Data Set (VSAM, sequential by arrival) |
| FD | File Description entry in COBOL Data Division |
| GDG | Generation Data Group -- collection of related dataset generations |
| HLQ | High-Level Qualifier -- first node of a dataset name |
| IDCAMS | Access Method Services utility for VSAM management |
| KSDS | Key-Sequenced Data Set (VSAM, indexed by primary key) |
| LDS | Linear Data Set (VSAM, byte-stream, no record structure) |
| LRECL | Logical Record Length |
| PDS | Partitioned Data Set (directory + members) |
| PDSE | Partitioned Data Set Extended (modern replacement for PDS) |
| PS | Physical Sequential organization |
| RECFM | Record Format (FB, VB, FBA, etc.) |
| RLSE | Release unused space at dataset close |
| RRDS | Relative Record Data Set (VSAM, accessed by slot number) |
| SMS | Storage Management Subsystem |
| VSAM | Virtual Storage Access Method |
| VTOC | Volume Table of Contents |
Exercises
-
Dataset Design: You are building a new loan processing system. Design the dataset architecture -- choose the organization, record format, and approximate space allocation for: (a) a loan master file keyed by loan number, (b) a daily payment transaction file, (c) a monthly statement report, (d) a regulatory extract file sent to an external agency.
-
GDG Management: Write the IDCAMS control statements to define a GDG base for daily account balance snapshots. The base should retain 60 generations. Then write JCL for a job that creates a new generation and reads the previous generation for comparison.
-
Error Diagnosis: A batch job abends with S013-10 on the DD statement for
ACCTMAST. The JCL specifiesDCB=(RECFM=FB,LRECL=350,BLKSIZE=27950). The COBOL FD saysRECORD CONTAINS 350 CHARACTERS. The dataset was created last month with LRECL=300. What caused the abend, and how do you fix it? -
COBOL-Dataset Integration: Write a complete COBOL program with JCL that reads a VSAM KSDS customer master file using dynamic access mode. The program should: (a) sequentially browse all records where customer state is 'NY', then (b) perform random reads for a list of specific customer IDs provided in an input file.
-
Space Calculation: A new dataset will contain 2,000,000 records, each 450 bytes, in FB format. Calculate: (a) the total data size in bytes, (b) the number of 3390 tracks needed, (c) the number of cylinders, (d) an appropriate SPACE parameter with 30% growth headroom.
In the next chapter, we will explore z/OS security concepts relevant to the COBOL programmer, including RACF profiles, dataset security, and the security implications of batch job submission and execution.