36 min read

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

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:

  1. Catalog location -- the HLQ maps to a user catalog through the master catalog
  2. Security -- RACF rules are typically based on HLQ patterns
  3. 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:

  1. Master catalog -- one per z/OS system. Contains entries for system datasets (SYS1., etc.) and aliases* that point to user catalogs.

  2. 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 name
  • LIMIT(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):

  1. The DD statement DCB parameter
  2. The dataset label (existing dataset's attributes stored in the VTOC)
  3. 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.

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

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

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

  3. Error Diagnosis: A batch job abends with S013-10 on the DD statement for ACCTMAST. The JCL specifies DCB=(RECFM=FB,LRECL=350,BLKSIZE=27950). The COBOL FD says RECORD CONTAINS 350 CHARACTERS. The dataset was created last month with LRECL=300. What caused the abend, and how do you fix it?

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

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