33 min read

> "The first database management system ever built for production use wasn't relational — it was hierarchical. And it's still running." — paraphrased from IBM Systems Journal archives

Chapter 31: IMS/DB Basics

"The first database management system ever built for production use wasn't relational — it was hierarchical. And it's still running." — paraphrased from IBM Systems Journal archives

When Derek Washington first joined GlobalBank, he assumed every database was relational. Tables, rows, columns, SQL — that was the world his university courses had prepared him for. Then Maria Chen handed him a maintenance ticket for the customer master file. "It's in IMS," she said, as if that explained everything. Derek stared at the screen full of DL/I calls, segment layouts, and status codes, and realized he'd entered a different universe — one that predated SQL by over a decade and still processed more transactions per day than most modern systems would ever see.

This chapter introduces you to that universe. IMS/DB (Information Management System/Database) is IBM's hierarchical database manager, born from the Apollo space program in the 1960s and still powering critical workloads at thousands of organizations worldwide. You will learn to navigate hierarchical data structures, issue DL/I calls from COBOL programs, interpret status codes, and understand why IMS remains relevant in an era of relational and NoSQL databases.

31.1 A Brief History: From the Moon to the Mainframe

IMS has one of the most remarkable origin stories in computing. In 1966, IBM partnered with North American Aviation (later Rockwell International) and Caterpillar Tractor to build a system that could manage the staggering bill of materials for the Saturn V rocket. The Apollo program needed to track millions of parts organized in hierarchical assemblies — a rocket contains stages, stages contain modules, modules contain components, components contain sub-components. A hierarchical database was the natural fit.

IBM released IMS Version 1 in 1968, making it one of the first general-purpose database management systems. Here is the timeline of key milestones:

Year Milestone
1966 IMS development begins for Apollo program
1968 IMS V1 released — hierarchical DB + message queuing
1969 Apollo 11 lands on the moon; IMS manages Saturn V BOM
1973 IMS/VS (Virtual Storage) — exploits MVS virtual memory
1977 Fast Path introduced for high-volume online transactions
1985 IMS supports shared databases across multiple systems
1990 IMS V3 with improved logging and recovery
1997 IMS V6 — HALDB (High Availability Large Database)
2002 IMS V8 — XML support
2005 IMS V9 — IMS Connect for TCP/IP access
2010 IMS V12 — Java dependent regions
2014 IMS V14 — RESTful services via IMS Connect
2017 IMS V15 — cloud integration, z/OS Connect EE
2021 IMS continues processing an estimated 50+ billion transactions per day worldwide

💡 Legacy != Obsolete: IMS processes more transactions daily than all the world's web servers combined, according to some industry estimates. When you hear "legacy database," don't think "outdated." Think "battle-tested at planetary scale." Many of the world's largest banks, insurers, airlines, and manufacturers still rely on IMS for their most critical data.

31.2 Hierarchical Database Concepts

Before we write a single line of COBOL, you need to understand how IMS organizes data. If you are coming from a relational background — which most students are — you will need to shift your mental model significantly.

31.2.1 The Hierarchical Model

In a relational database, data lives in flat tables connected by foreign keys. In IMS, data lives in segments organized into hierarchies (tree structures). Every IMS database is a collection of one or more database records, each of which is a tree rooted at a root segment.

Think of it like a family tree:

           CUSTOMER (root segment)
          /         \
     ACCOUNT       ADDRESS
    /       \
TRANSACTION  LOAN

Key terminology:

  • Segment: The basic unit of data in IMS, analogous (loosely) to a row in a relational table. Each segment has a fixed or variable-length layout defined in a DBD (Database Description).
  • Segment type: A category of segments — like a table definition. In the hierarchy above, CUSTOMER, ACCOUNT, ADDRESS, TRANSACTION, and LOAN are segment types.
  • Segment occurrence: A specific instance of a segment type — like a row. Customer "John Smith" is a segment occurrence of the CUSTOMER segment type.
  • Root segment: The topmost segment in a hierarchy. Every database record begins with exactly one root segment occurrence.
  • Parent/Child: A segment that has segments beneath it is a parent; the segments beneath are its children. CUSTOMER is the parent of ACCOUNT and ADDRESS. ACCOUNT is the parent of TRANSACTION and LOAN.
  • Twin segments: Multiple occurrences of the same segment type under the same parent. If a customer has three accounts, those three ACCOUNT segments are twins.
  • Database record: The complete tree starting from one root segment occurrence through all its descendants. All of John Smith's data — his customer segment, all his accounts, all transactions under those accounts, all his addresses — form one database record.

31.2.2 Hierarchical Sequence

IMS processes segments in a specific order called hierarchical sequence. This is the order in which IMS traverses the tree if you simply say "give me the next segment." The traversal is top-down, left-to-right, depth-first:

           CUSTOMER (1)
          /           \
     ACCOUNT (2)     ADDRESS (6)
    /         \
TRANS (3)   LOAN (5)
TRANS (4)

The numbers show the hierarchical sequence. IMS would visit: 1. CUSTOMER 2. First ACCOUNT 3. First TRANSACTION under that account 4. Second TRANSACTION under that account 5. LOAN under that account 6. ADDRESS

This sequence is crucial for understanding how the Get Next (GN) call works — it simply moves forward through this sequence.

31.2.3 Physical vs. Logical Databases

IMS supports two types of databases:

Physical databases represent the actual storage of data. The hierarchy is stored on disk using one of several access methods: - HSAM (Hierarchical Sequential Access Method) — sequential, like a tape file; rarely used today - HISAM (Hierarchical Indexed Sequential Access Method) — indexed access to root segments, sequential for dependents - HDAM (Hierarchical Direct Access Method) — uses a randomizing module (hash) for root segments; fast direct access - HIDAM (Hierarchical Indexed Direct Access Method) — uses an index for root segments; supports sequential and direct access

Logical databases allow you to create virtual views that combine segments from multiple physical databases. For example, you might have one physical database for customers and another for products, but create a logical relationship between them so a program can navigate from a customer order segment to a product segment as if they were in the same hierarchy.

⚠️ Important: Logical databases are a DBA concern. As a COBOL programmer, you code against the database as described in the PSB (Program Specification Block) provided to you. Whether it is physical or logical is largely transparent to your program.

31.2.4 Pointers and Physical Storage

Under the covers, IMS uses pointers to connect segments. The type of pointer chain affects performance:

  • Hierarchical pointers: Connect parent to first child, and each child to the next twin (sibling)
  • Physical child/physical twin pointers: Standard pointer pairs
  • Logical pointers: Connect segments across physical databases

You do not manipulate pointers directly in your COBOL program — the DBA defines them in the DBD. But understanding that they exist helps you appreciate why certain access patterns are fast (following pointers down a path) and others are slow (scanning across many database records).

📊 Relational vs. Hierarchical — Quick Comparison:

Concept Relational (DB2) Hierarchical (IMS)
Data unit Row Segment
Data container Table Segment type in hierarchy
Schema definition DDL (CREATE TABLE) DBD (Database Description)
Access authorization GRANT/REVOKE PSB (Program Specification Block)
Query language SQL DL/I calls
Relationship Foreign keys, JOINs Parent-child hierarchy
Normalization 1NF through BCNF typical Denormalization common
Transaction COMMIT/ROLLBACK CHKP/SYNC points

31.3 The DL/I Call Interface

DL/I (Data Language/Interface) is the API you use to access IMS databases from COBOL. Unlike SQL, which is declarative ("give me all customers in New York"), DL/I is navigational — you tell IMS exactly how to move through the hierarchy, one segment at a time.

31.3.1 The CALL Statement

Every DL/I operation is issued through a CALL to the IMS interface module, CBLTDLI (for batch) or AIBTDLI (for newer programs using the AIB interface). The general format:

       CALL 'CBLTDLI' USING function-code
                             pcb-name
                             io-area
                             ssa-1
                             ssa-2
                             ...

The parameters are: - Function code: A 4-byte field telling IMS what operation to perform (GU, GN, GNP, ISRT, DLET, REPL, etc.) - PCB name: Which Program Communication Block to use (identifies the database) - I/O area: A WORKING-STORAGE area where IMS places retrieved segments or where you place segments to insert - SSAs: Segment Search Arguments — zero or more, telling IMS which segments to access

Let us define the standard function codes in WORKING-STORAGE:

       WORKING-STORAGE SECTION.

       01  WS-DLI-FUNCTIONS.
           05  DLI-GU            PIC X(4) VALUE 'GU  '.
           05  DLI-GN            PIC X(4) VALUE 'GN  '.
           05  DLI-GNP           PIC X(4) VALUE 'GNP '.
           05  DLI-GHU           PIC X(4) VALUE 'GHU '.
           05  DLI-GHN           PIC X(4) VALUE 'GHN '.
           05  DLI-GHNP          PIC X(4) VALUE 'GHNP'.
           05  DLI-ISRT          PIC X(4) VALUE 'ISRT'.
           05  DLI-DLET          PIC X(4) VALUE 'DLET'.
           05  DLI-REPL          PIC X(4) VALUE 'REPL'.
           05  DLI-CHKP          PIC X(4) VALUE 'CHKP'.

31.3.2 Retrieval Calls — The Get Family

IMS provides six retrieval calls, organized along two axes: qualified vs. sequential and hold vs. no-hold.

Get Unique (GU) — Direct retrieval. Jumps directly to a specific segment based on fully qualified SSAs. Like a SQL SELECT ... WHERE primary_key = value.

       CALL 'CBLTDLI' USING DLI-GU
                             CUSTOMER-PCB
                             CUSTOMER-IO-AREA
                             CUSTOMER-SSA.

Get Next (GN) — Sequential retrieval. Moves forward in hierarchical sequence from the current position. Like a cursor FETCH NEXT, but through the entire database hierarchy.

       CALL 'CBLTDLI' USING DLI-GN
                             CUSTOMER-PCB
                             ACCOUNT-IO-AREA
                             ACCOUNT-SSA.

Get Next within Parent (GNP) — Sequential retrieval constrained to the current parent. If you are positioned on a customer, GNP retrieves the next child segment (account, address) under that customer only. When all children are exhausted, you get a GE (not found) status.

       CALL 'CBLTDLI' USING DLI-GNP
                             CUSTOMER-PCB
                             TRANSACTION-IO-AREA
                             TRANSACTION-SSA.

Hold variants (GHU, GHN, GHNP) — Identical to GU, GN, GNP, but they establish a hold on the retrieved segment, allowing you to subsequently issue REPL (replace) or DLET (delete). You must use a hold call before updating or deleting.

💡 Key Rule: You cannot REPL or DLET a segment unless you retrieved it with a hold call (GHU, GHN, or GHNP). This is IMS's way of ensuring you have a current, valid position on the segment you intend to modify.

31.3.3 Update Calls

ISRT (Insert) — Adds a new segment occurrence to the database. You fill the I/O area with the segment data and specify SSAs to indicate where in the hierarchy the new segment should be placed.

      * Insert a new account under a customer
       MOVE 'SAVINGS'       TO ACCT-TYPE-IO
       MOVE 50000           TO ACCT-BALANCE-IO
       MOVE '2025-01-15'    TO ACCT-OPEN-DATE-IO

       CALL 'CBLTDLI' USING DLI-ISRT
                             CUSTOMER-PCB
                             ACCOUNT-IO-AREA
                             CUSTOMER-QUAL-SSA
                             ACCOUNT-UNQUAL-SSA.

REPL (Replace) — Updates the segment most recently retrieved with a hold call. You modify the I/O area and issue REPL. You must not change the segment's key field.

      * Retrieve account with hold
       CALL 'CBLTDLI' USING DLI-GHU
                             CUSTOMER-PCB
                             ACCOUNT-IO-AREA
                             CUSTOMER-QUAL-SSA
                             ACCOUNT-QUAL-SSA.

       IF PCB-STATUS-CODE = SPACES
           ADD 1000 TO ACCT-BALANCE-IO
           CALL 'CBLTDLI' USING DLI-REPL
                                 CUSTOMER-PCB
                                 ACCOUNT-IO-AREA
       END-IF.

DLET (Delete) — Deletes the segment most recently retrieved with a hold call and all its dependents. If you delete an ACCOUNT segment, all TRANSACTION and LOAN segments under it are also deleted.

⚠️ Warning: DLET is cascading. Deleting a parent segment removes the entire subtree beneath it. There is no "ON DELETE SET NULL" option as in DB2. Always verify your position before issuing DLET.

31.3.4 Other Important Calls

  • CHKP (Checkpoint) — Establishes a sync point; commits all database changes since the last checkpoint
  • XRST (Extended Restart) — Used with CHKP for restart/recovery in batch programs
  • ROLB (Rollback) — Backs out changes to the last checkpoint
  • PCB (Schedule/Terminate) — In some environments, explicitly schedules or terminates database access

31.4 PCBs and PSBs — Your Database Passport

Before your COBOL program can access any IMS database, a DBA must create two key definitions: the DBD (Database Description) and the PSB (Program Specification Block).

31.4.1 Database Description (DBD)

The DBD defines the physical structure of an IMS database — segment types, their fields, their parent-child relationships, and the access method. Think of it as the equivalent of CREATE TABLE statements in DB2, but for an entire hierarchy.

A simplified DBD for GlobalBank's customer database:

         DBD    NAME=CUSTDB,ACCESS=HIDAM
         SEGM   NAME=CUSTOMER,BYTES=250,PARENT=0
         FIELD  NAME=(CUST-ID,SEQ,U),BYTES=10,START=1,TYPE=C
         FIELD  NAME=CUST-NAME,BYTES=40,START=11,TYPE=C
         FIELD  NAME=CUST-SSN,BYTES=9,START=51,TYPE=C
         SEGM   NAME=ACCOUNT,PARENT=CUSTOMER,BYTES=150
         FIELD  NAME=(ACCT-NUM,SEQ,U),BYTES=12,START=1,TYPE=C
         FIELD  NAME=ACCT-TYPE,BYTES=10,START=13,TYPE=C
         FIELD  NAME=ACCT-BALANCE,BYTES=8,START=23,TYPE=P
         SEGM   NAME=TRANSACT,PARENT=ACCOUNT,BYTES=100
         FIELD  NAME=(TRANS-DATE,SEQ),BYTES=8,START=1,TYPE=C
         FIELD  NAME=TRANS-AMT,BYTES=8,START=9,TYPE=P
         FIELD  NAME=TRANS-TYPE,BYTES=2,START=17,TYPE=C
         SEGM   NAME=ADDRESS,PARENT=CUSTOMER,BYTES=200
         FIELD  NAME=(ADDR-TYPE,SEQ,U),BYTES=4,START=1,TYPE=C
         DBDGEN
         FINISH
         END

You will not code DBDs as a COBOL programmer — that is the DBA's job. But you must understand the hierarchy they define to write correct DL/I calls.

31.4.2 Program Communication Block (PCB)

A PCB defines your program's view of one IMS database. It specifies: - Which database you can access - Which segment types you can see (sensitivity) - What operations you can perform (read, insert, replace, delete) - Where IMS reports status information back to you

The PCB is your main feedback mechanism from IMS. After every DL/I call, IMS updates the PCB with: - Status code (2 bytes) — the result of the call - Segment name (8 bytes) — the segment type just accessed - Key feedback area — the concatenated key of the current position

In your COBOL program, you define the PCB in the LINKAGE SECTION:

       LINKAGE SECTION.

       01  CUSTOMER-PCB.
           05  PCB-DBD-NAME       PIC X(8).
           05  PCB-SEG-LEVEL      PIC XX.
           05  PCB-STATUS-CODE    PIC XX.
           05  PCB-PROC-OPTIONS   PIC X(4).
           05  FILLER             PIC S9(5) COMP.
           05  PCB-SEG-NAME       PIC X(8).
           05  PCB-KEY-LENGTH     PIC S9(5) COMP.
           05  PCB-NUM-SENS-SEGS  PIC S9(5) COMP.
           05  PCB-KEY-FEEDBACK   PIC X(50).

31.4.3 Program Specification Block (PSB)

The PSB groups all the PCBs your program needs. If your program accesses two databases — the customer database and the provider database — your PSB will contain two PCBs.

The PSB also specifies sensitivity at the segment level. Perhaps your program only needs to read CUSTOMER and ACCOUNT segments but not TRANSACTION segments. The PSB would list sensitivity for only those two segment types. This is both a security mechanism (preventing unauthorized access) and a documentation tool (making explicit what the program uses).

         PCB    TYPE=DB,DBDNAME=CUSTDB,PROCOPT=A,KEYLEN=22
         SENSEG NAME=CUSTOMER,PROCOPT=G
         SENSEG NAME=ACCOUNT,PROCOPT=GR
         SENSEG NAME=TRANSACT,PROCOPT=GI
         PSBGEN LANG=COBOL,PSBNAME=BNKACCT1
         END

PROCOPT values: - G = Get (read) - I = Insert - R = Replace - D = Delete - A = All (GIRD)

31.4.4 The ENTRY Statement and PSB Mask

In a batch DL/I program, your program receives the PSB through the ENTRY statement (or GOBACK entry convention). The standard entry coding:

       PROCEDURE DIVISION.

       ENTRY 'DLITCBL' USING CUSTOMER-PCB.

       MAIN-PROCESS.
           PERFORM PROCESS-CUSTOMERS
           GOBACK.

For programs with multiple PCBs:

       ENTRY 'DLITCBL' USING IO-PCB
                              CUSTOMER-PCB
                              PROVIDER-PCB.

The IO-PCB is a special PCB automatically provided by IMS for issuing checkpoint and system service calls. It is always the first PCB in the list.

31.5 Segment Search Arguments (SSAs)

SSAs are the "WHERE clause" of DL/I. They tell IMS which segments you want to retrieve, insert into, or navigate through.

31.5.1 Unqualified SSAs

An unqualified SSA simply names a segment type, with no selection criteria. It says "any occurrence of this segment type."

Format: segment name in bytes 1-8, followed by a blank in byte 9.

       01  CUSTOMER-UNQUAL-SSA.
           05  FILLER          PIC X(9) VALUE 'CUSTOMER '.

       01  ACCOUNT-UNQUAL-SSA.
           05  FILLER          PIC X(9) VALUE 'ACCOUNT  '.

       01  TRANS-UNQUAL-SSA.
           05  FILLER          PIC X(9) VALUE 'TRANSACT '.

31.5.2 Qualified SSAs

A qualified SSA includes a selection criterion — the equivalent of a WHERE clause. The format uses a specific structure:

SegName(FieldName operator value)

In COBOL, this is typically defined as:

       01  CUSTOMER-QUAL-SSA.
           05  FILLER          PIC X(9) VALUE 'CUSTOMER('.
           05  FILLER          PIC X(10) VALUE 'CUST-ID  ='.
           05  CQ-CUST-ID     PIC X(10).
           05  FILLER          PIC X(1) VALUE ')'.

The qualification operators are: - = or EQ — equal to - > or GT — greater than - < or LT — less than - >= or GE — greater than or equal - <= or LE — less than or equal - != or NE — not equal

You can combine multiple qualifications with Boolean operators: - * or & — AND - + or | — OR

       01  ACCT-QUAL-SSA.
           05  FILLER          PIC X(9) VALUE 'ACCOUNT ('.
           05  FILLER          PIC X(12)
               VALUE 'ACCT-NUM   ='.
           05  AQ-ACCT-NUM    PIC X(12).
           05  FILLER          PIC X(1) VALUE ')'.

31.5.3 Command Codes

Command codes modify how IMS processes a DL/I call. They are inserted into the SSA after the segment name, preceded by an asterisk:

SegName*command_code(qualification)

Key command codes:

Code Name Purpose
D Path Retrieve multiple segments in a single call
F First Position to the first occurrence
L Last Position to the last occurrence
N Path ignore Move through hierarchy without checking this level
P Set parentage Establish this segment as the current parent
U Maintain position Maintain position at this level after the call
V Maintain position Maintain position at this and all higher levels

The D (Path) command code is particularly useful. It allows you to retrieve all segments in a path with a single call:

       01  CUST-PATH-SSA.
           05  FILLER          PIC X(9) VALUE 'CUSTOMER*'.
           05  FILLER          PIC X(1) VALUE 'D'.
           05  FILLER          PIC X(1) VALUE '('.
           05  FILLER          PIC X(10) VALUE 'CUST-ID  ='.
           05  CP-CUST-ID     PIC X(10).
           05  FILLER          PIC X(1) VALUE ')'.

       01  ACCT-PATH-SSA.
           05  FILLER          PIC X(9) VALUE 'ACCOUNT *'.
           05  FILLER          PIC X(1) VALUE 'D'.
           05  FILLER          PIC X(1) VALUE '('.
           05  FILLER          PIC X(12)
               VALUE 'ACCT-NUM   ='.
           05  AP-ACCT-NUM    PIC X(12).
           05  FILLER          PIC X(1) VALUE ')'.

With D command codes on both SSAs, a single GU call retrieves both the CUSTOMER and ACCOUNT segments into a concatenated I/O area.

31.6 Status Codes — Interpreting IMS Responses

After every DL/I call, IMS places a two-character status code in the PCB. Your program must check this status code after every call. This is not optional — it is the single most important defensive programming practice in IMS.

31.6.1 Common Status Codes

Code Meaning When It Occurs
(spaces) Successful Call completed normally
GE Segment not found GU: no matching segment; GN/GNP: end of database/parent
II Segment already exists ISRT: duplicate key
DA Data/key not valid Sequence field violation
DJ Segment type mismatch SSA names wrong segment
AI OPEN failure Database could not be opened
AO I/O error Physical I/O error on the database
AM LRECL error Logical record length mismatch
AB ABEND IMS forced an abnormal termination
GA Change of parent GN: moved to a segment under a different parent
GK Change in segment type GN: returned a different segment type but same parent

31.6.2 Defensive Status Code Handling

Every DL/I call in a production program should have status code checking. Here is the pattern Maria Chen teaches at GlobalBank:

       CALL 'CBLTDLI' USING DLI-GU
                             CUSTOMER-PCB
                             CUSTOMER-IO-AREA
                             CUSTOMER-QUAL-SSA.

       EVALUATE PCB-STATUS-CODE
           WHEN SPACES
               CONTINUE
           WHEN 'GE'
               MOVE 'CUSTOMER NOT FOUND' TO WS-ERROR-MSG
               PERFORM ERROR-HANDLER
           WHEN OTHER
               STRING 'UNEXPECTED IMS STATUS: '
                      PCB-STATUS-CODE
                      ' ON CUSTOMER GU'
                      DELIMITED BY SIZE
                      INTO WS-ERROR-MSG
               PERFORM ABEND-HANDLER
       END-EVALUATE.

🔴 Critical Rule: Never assume a DL/I call succeeded. Always check the status code. The most common production bug in IMS programs is failing to check for GE status, which causes the program to process stale data from a previous call's I/O area.

31.6.3 The GA and GK Status Codes

GA and GK are informational status codes returned on GN calls. They tell you that IMS successfully retrieved a segment, but the hierarchical position has changed:

  • GA: The segment is under a different parent than the previous segment. For example, you moved from transactions under Account 001 to transactions under Account 002.
  • GK: The segment is a different type than the previous one, but under the same parent. For example, you moved from an ACCOUNT segment to an ADDRESS segment under the same customer.

These are not errors — IMS still retrieved a valid segment. But they are informational signals that help you detect boundaries when processing sequentially.

       PERFORM UNTIL WS-END-OF-ACCOUNTS
           CALL 'CBLTDLI' USING DLI-GNP
                                 CUSTOMER-PCB
                                 ACCOUNT-IO-AREA
                                 ACCOUNT-UNQUAL-SSA

           EVALUATE PCB-STATUS-CODE
               WHEN SPACES
                   PERFORM PROCESS-ACCOUNT
               WHEN 'GE'
                   SET WS-END-OF-ACCOUNTS TO TRUE
               WHEN 'GK'
      *            Different segment type under same customer
      *            We only want accounts, so skip
                   CONTINUE
               WHEN OTHER
                   PERFORM ABEND-HANDLER
           END-EVALUATE
       END-PERFORM.

31.7 IMS Regions and Execution Environments

IMS programs run in different types of regions (address spaces), each with different characteristics:

31.7.1 Batch DL/I

The simplest environment. Your COBOL program runs as a standard batch job, accessing IMS databases through DL/I calls. The program has exclusive or shared access to the database, runs to completion, and terminates.

//IMSBTCH  EXEC PGM=DFSRRC00,PARM='DLI,CUSTINQ1,CUSTPSB'
//STEPLIB  DD DSN=IMS.SDFSRESL,DISP=SHR
//IMS      DD DSN=IMS.DBDLIB,DISP=SHR
//         DD DSN=IMS.PSBLIB,DISP=SHR
//CUSTDB   DD DSN=GBANK.CUST.DB,DISP=SHR
//SYSPRINT DD SYSOUT=*

Characteristics: - Program starts, processes, and ends - No message processing - Checkpoint/restart for recovery - Typically used for batch updates, reports, data extracts

31.7.2 BMP (Batch Message Processing)

A BMP program is a batch program that runs in the IMS online environment. It can access IMS databases and IMS message queues, and it participates in IMS's online recovery mechanisms.

Characteristics: - Runs as a batch job but within IMS control - Can access databases with full IMS logging and recovery - Can process messages from IMS message queues - Better recovery than pure batch DL/I - Can run concurrently with online IMS transactions

31.7.3 MPP (Message Processing Program)

An MPP is an online transaction program. It receives a message from a terminal (or IMS Connect), processes it, and sends a response. This is the IMS equivalent of a CICS transaction program.

Characteristics: - Triggered by an incoming message (transaction code) - Must respond quickly — IMS monitors response times - Processes one message at a time - Participates fully in IMS sync point and recovery - IMS schedules and manages the program lifecycle

31.7.4 IFP (IMS Fast Path)

Fast Path programs process extremely high-volume, short-running transactions using specialized Fast Path databases (DEDBs — Data Entry Databases) and MSDBs (Main Storage Databases). These are the IMS programs processing millions of ATM transactions per second at the world's largest banks.

📊 Choosing the Right Region Type:

Criterion Batch DL/I BMP MPP IFP
Recovery Manual CHKP IMS-managed Automatic Automatic
Throughput Low-Medium Medium High Very High
Response time N/A N/A Important Critical
Message processing No Optional Required Required
Use case Reports, ETL Batch updates Online inquiry High-volume online

31.8 A Complete IMS COBOL Program

Let us build a complete batch DL/I program for GlobalBank that retrieves customer information and prints all their accounts. This is the program Derek Washington needs to write for his first IMS assignment.

      *================================================================*
      * PROGRAM: CUSTINQ1                                              *
      * PURPOSE: Retrieve customer and account information from IMS    *
      * AUTHOR:  Derek Washington / GlobalBank Development             *
      * DATE:    2025-06-15                                            *
      *================================================================*
       IDENTIFICATION DIVISION.
       PROGRAM-ID.  CUSTINQ1.

       ENVIRONMENT DIVISION.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

      *--- DL/I Function Codes ---*
       01  WS-DLI-FUNCTIONS.
           05  DLI-GU           PIC X(4) VALUE 'GU  '.
           05  DLI-GN           PIC X(4) VALUE 'GN  '.
           05  DLI-GNP          PIC X(4) VALUE 'GNP '.
           05  DLI-GHU          PIC X(4) VALUE 'GHU '.

      *--- SSAs ---*
       01  CUSTOMER-QUAL-SSA.
           05  FILLER           PIC X(9) VALUE 'CUSTOMER('.
           05  FILLER           PIC X(10) VALUE 'CUST-ID  ='.
           05  SSA-CUST-ID     PIC X(10).
           05  FILLER           PIC X(1) VALUE ')'.

       01  ACCOUNT-UNQUAL-SSA.
           05  FILLER           PIC X(9) VALUE 'ACCOUNT  '.

      *--- I/O Areas ---*
       01  CUSTOMER-IO-AREA.
           05  CIO-CUST-ID     PIC X(10).
           05  CIO-CUST-NAME   PIC X(40).
           05  CIO-CUST-SSN    PIC X(9).
           05  CIO-CUST-DOB    PIC X(10).
           05  CIO-CUST-TYPE   PIC X(2).
           05  CIO-CUST-STATUS PIC X(1).
           05  FILLER           PIC X(178).

       01  ACCOUNT-IO-AREA.
           05  AIO-ACCT-NUM    PIC X(12).
           05  AIO-ACCT-TYPE   PIC X(10).
           05  AIO-ACCT-BAL    PIC S9(11)V99 COMP-3.
           05  AIO-OPEN-DATE   PIC X(10).
           05  AIO-ACCT-STATUS PIC X(1).
           05  FILLER           PIC X(110).

      *--- Work Fields ---*
       01  WS-WORK-FIELDS.
           05  WS-CUST-ID-INPUT PIC X(10).
           05  WS-ACCT-COUNT    PIC 9(3).
           05  WS-TOTAL-BAL     PIC S9(13)V99.
           05  WS-DISPLAY-BAL   PIC Z(10)9.99-.
           05  WS-ERROR-MSG     PIC X(80).
           05  WS-MORE-ACCTS-SW PIC X(1).
               88 MORE-ACCOUNTS      VALUE 'Y'.
               88 NO-MORE-ACCOUNTS   VALUE 'N'.

       LINKAGE SECTION.

       01  IO-PCB.
           05  FILLER           PIC X(10).

       01  CUSTOMER-PCB.
           05  PCB-DBD-NAME     PIC X(8).
           05  PCB-SEG-LEVEL    PIC XX.
           05  PCB-STATUS-CODE  PIC XX.
           05  PCB-PROC-OPTIONS PIC X(4).
           05  FILLER           PIC S9(5) COMP.
           05  PCB-SEG-NAME     PIC X(8).
           05  PCB-KEY-LENGTH   PIC S9(5) COMP.
           05  PCB-NUM-SENS     PIC S9(5) COMP.
           05  PCB-KEY-FEEDBACK PIC X(50).

       PROCEDURE DIVISION.

       ENTRY 'DLITCBL' USING IO-PCB
                              CUSTOMER-PCB.

       0000-MAIN.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS-INQUIRY
           PERFORM 9000-TERMINATE
           GOBACK.

       1000-INITIALIZE.
           MOVE ZEROS TO WS-ACCT-COUNT
           MOVE ZEROS TO WS-TOTAL-BAL
           MOVE 'C000010001' TO WS-CUST-ID-INPUT
           DISPLAY '=== GLOBALBANK CUSTOMER INQUIRY ==='
           DISPLAY ' '.

       2000-PROCESS-INQUIRY.
           PERFORM 2100-GET-CUSTOMER
           IF PCB-STATUS-CODE = SPACES
               PERFORM 2200-DISPLAY-CUSTOMER
               PERFORM 2300-GET-ALL-ACCOUNTS
               PERFORM 2400-DISPLAY-SUMMARY
           ELSE
               DISPLAY 'CUSTOMER NOT FOUND: '
                       WS-CUST-ID-INPUT
           END-IF.

       2100-GET-CUSTOMER.
           MOVE WS-CUST-ID-INPUT TO SSA-CUST-ID

           CALL 'CBLTDLI' USING DLI-GU
                                 CUSTOMER-PCB
                                 CUSTOMER-IO-AREA
                                 CUSTOMER-QUAL-SSA.

           IF PCB-STATUS-CODE NOT = SPACES
              AND PCB-STATUS-CODE NOT = 'GE'
               STRING 'IMS ERROR ON CUSTOMER GU: '
                      PCB-STATUS-CODE
                      DELIMITED BY SIZE
                      INTO WS-ERROR-MSG
               DISPLAY WS-ERROR-MSG
               PERFORM 9999-ABEND
           END-IF.

       2200-DISPLAY-CUSTOMER.
           DISPLAY 'CUSTOMER ID:   ' CIO-CUST-ID
           DISPLAY 'NAME:          ' CIO-CUST-NAME
           DISPLAY 'DATE OF BIRTH: ' CIO-CUST-DOB
           DISPLAY 'TYPE:          ' CIO-CUST-TYPE
           DISPLAY 'STATUS:        ' CIO-CUST-STATUS
           DISPLAY ' '.
           DISPLAY '--- ACCOUNTS ---'.

       2300-GET-ALL-ACCOUNTS.
           SET MORE-ACCOUNTS TO TRUE
           PERFORM UNTIL NO-MORE-ACCOUNTS
               CALL 'CBLTDLI' USING DLI-GNP
                                     CUSTOMER-PCB
                                     ACCOUNT-IO-AREA
                                     ACCOUNT-UNQUAL-SSA

               EVALUATE PCB-STATUS-CODE
                   WHEN SPACES
                       ADD 1 TO WS-ACCT-COUNT
                       ADD AIO-ACCT-BAL TO WS-TOTAL-BAL
                       PERFORM 2310-DISPLAY-ACCOUNT
                   WHEN 'GE'
                       SET NO-MORE-ACCOUNTS TO TRUE
                   WHEN 'GK'
      *                Different segment type (e.g., ADDRESS)
      *                We only want ACCOUNT, so skip
                       CONTINUE
                   WHEN OTHER
                       STRING 'IMS ERROR ON ACCOUNT GNP: '
                              PCB-STATUS-CODE
                              DELIMITED BY SIZE
                              INTO WS-ERROR-MSG
                       DISPLAY WS-ERROR-MSG
                       PERFORM 9999-ABEND
               END-EVALUATE
           END-PERFORM.

       2310-DISPLAY-ACCOUNT.
           MOVE AIO-ACCT-BAL TO WS-DISPLAY-BAL
           DISPLAY '  ACCOUNT: ' AIO-ACCT-NUM
                   '  TYPE: '   AIO-ACCT-TYPE
                   '  BAL: '    WS-DISPLAY-BAL
                   '  STATUS: ' AIO-ACCT-STATUS.

       2400-DISPLAY-SUMMARY.
           DISPLAY ' '
           DISPLAY 'TOTAL ACCOUNTS: ' WS-ACCT-COUNT
           MOVE WS-TOTAL-BAL TO WS-DISPLAY-BAL
           DISPLAY 'TOTAL BALANCE:  ' WS-DISPLAY-BAL.

       9000-TERMINATE.
           DISPLAY ' '
           DISPLAY '=== END OF INQUIRY ==='.

       9999-ABEND.
           DISPLAY 'FATAL ERROR — PROGRAM ABORTING'
           DISPLAY WS-ERROR-MSG
           CALL 'CEE3ABD' USING 4095 0.

Try It Yourself: In your Student Mainframe Lab (or using IBM Z Xplore), create a simple IMS database with CUSTOMER and ACCOUNT segments. Write a program that retrieves a customer and lists their accounts. If you are using GnuCOBOL without IMS, simulate the DL/I interface with a flat file reader — the logic patterns are the same even without a real IMS system.

31.9 Navigating Complex Hierarchies

Real-world IMS databases can be deep and wide. GlobalBank's full customer database hierarchy looks like this:

CUSTOMER (root)
├── ACCOUNT
│   ├── TRANSACTION
│   ├── LOAN
│   │   └── PAYMENT
│   └── HOLD
├── ADDRESS
├── PHONE
└── DOCUMENT
    └── DOC-IMAGE

31.9.1 Multi-Level Retrieval

To retrieve a specific transaction under a specific account under a specific customer, you provide SSAs at each level:

       CALL 'CBLTDLI' USING DLI-GU
                             CUSTOMER-PCB
                             TRANSACTION-IO-AREA
                             CUSTOMER-QUAL-SSA
                             ACCOUNT-QUAL-SSA
                             TRANS-QUAL-SSA.

IMS navigates down the hierarchy: find the customer, find the account under that customer, find the transaction under that account.

31.9.2 Mixed Qualified and Unqualified SSAs

You can mix qualified and unqualified SSAs. To get the first transaction under a specific account:

       CALL 'CBLTDLI' USING DLI-GU
                             CUSTOMER-PCB
                             TRANSACTION-IO-AREA
                             CUSTOMER-QUAL-SSA
                             ACCOUNT-QUAL-SSA
                             TRANS-UNQUAL-SSA.

The unqualified SSA at the TRANSACTION level says "any transaction will do" — IMS returns the first one in hierarchical sequence.

31.9.3 Processing All Transactions for a Customer

A common pattern is processing all transactions across all accounts for a given customer. Here is how you combine GU (to position on the customer) with GNP (to iterate through all descendants):

       PERFORM 3100-POSITION-ON-CUSTOMER
       IF PCB-STATUS-CODE = SPACES
           SET MORE-SEGMENTS TO TRUE
           PERFORM UNTIL NO-MORE-SEGMENTS
               CALL 'CBLTDLI' USING DLI-GNP
                                     CUSTOMER-PCB
                                     SEGMENT-IO-AREA

               EVALUATE PCB-STATUS-CODE
                   WHEN SPACES
                       PERFORM 3200-PROCESS-SEGMENT
                   WHEN 'GE'
                       SET NO-MORE-SEGMENTS TO TRUE
                   WHEN OTHER
                       PERFORM 9999-ABEND
               END-EVALUATE
           END-PERFORM
       END-IF.

In paragraph 3200-PROCESS-SEGMENT, you check PCB-SEG-NAME to determine what type of segment was returned:

       3200-PROCESS-SEGMENT.
           EVALUATE PCB-SEG-NAME
               WHEN 'ACCOUNT '
                   MOVE SEGMENT-IO-AREA TO ACCOUNT-IO-AREA
                   PERFORM 3210-PROCESS-ACCOUNT
               WHEN 'TRANSACT'
                   MOVE SEGMENT-IO-AREA TO TRANSACTION-IO-AREA
                   PERFORM 3220-PROCESS-TRANSACTION
               WHEN 'LOAN    '
                   MOVE SEGMENT-IO-AREA TO LOAN-IO-AREA
                   PERFORM 3230-PROCESS-LOAN
               WHEN 'ADDRESS '
                   MOVE SEGMENT-IO-AREA TO ADDRESS-IO-AREA
                   PERFORM 3240-PROCESS-ADDRESS
               WHEN OTHER
                   CONTINUE
           END-EVALUATE.

🔗 Cross-Reference: This navigational pattern — positioning, then iterating — is fundamentally different from SQL's set-based approach (Chapter 28). In SQL, you say SELECT * FROM transactions WHERE customer_id = :id. In DL/I, you position on the customer and then walk through the tree. Each approach has trade-offs.

31.10 MedClaim: Legacy Provider Database in IMS

At MedClaim Health Services, the provider database is one of the oldest systems still in production. James Okafor has maintained it for twelve years. The hierarchy:

PROVIDER (root)
├── SPECIALTY
├── LOCATION
│   └── SCHEDULE
├── CONTRACT
│   └── FEE-SCHEDULE
└── CREDENTIAL

When a claim comes in, the adjudication program must verify that the provider is credentialed, in-network, and that the billed amount matches the contracted fee schedule. This requires navigating multiple paths through the hierarchy.

      *================================================================*
      * Verify provider for claim adjudication                         *
      *================================================================*
       3000-VERIFY-PROVIDER.
      *--- Get provider root segment ---*
           MOVE CLM-PROVIDER-ID TO SSA-PROV-ID

           CALL 'CBLTDLI' USING DLI-GU
                                 PROVIDER-PCB
                                 PROVIDER-IO-AREA
                                 PROVIDER-QUAL-SSA

           IF PCB-STATUS-CODE NOT = SPACES
               MOVE 'PROV-NOT-FOUND' TO CLM-DENY-REASON
               SET CLAIM-DENIED TO TRUE
               GO TO 3000-EXIT
           END-IF

      *--- Check active credential ---*
           MOVE 'ACTIVE  ' TO SSA-CRED-STATUS

           CALL 'CBLTDLI' USING DLI-GNP
                                 PROVIDER-PCB
                                 CREDENTIAL-IO-AREA
                                 CREDENTIAL-QUAL-SSA

           IF PCB-STATUS-CODE NOT = SPACES
               MOVE 'PROV-NO-CRED' TO CLM-DENY-REASON
               SET CLAIM-DENIED TO TRUE
               GO TO 3000-EXIT
           END-IF

      *--- Find matching contract ---*
           MOVE CLM-PLAN-CODE TO SSA-CONTRACT-PLAN

           CALL 'CBLTDLI' USING DLI-GU
                                 PROVIDER-PCB
                                 CONTRACT-IO-AREA
                                 PROVIDER-QUAL-SSA
                                 CONTRACT-QUAL-SSA

           IF PCB-STATUS-CODE NOT = SPACES
               MOVE 'PROV-NO-CONTRACT' TO CLM-DENY-REASON
               SET CLAIM-DENIED TO TRUE
               GO TO 3000-EXIT
           END-IF

      *--- Get fee schedule for this procedure ---*
           MOVE CLM-PROC-CODE TO SSA-FEE-PROC

           CALL 'CBLTDLI' USING DLI-GNP
                                 PROVIDER-PCB
                                 FEE-IO-AREA
                                 FEE-QUAL-SSA

           IF PCB-STATUS-CODE NOT = SPACES
               MOVE 'NO-FEE-SCHEDULE' TO CLM-DENY-REASON
               SET CLAIM-DENIED TO TRUE
               GO TO 3000-EXIT
           END-IF

      *--- Compare billed amount to allowed amount ---*
           IF CLM-BILLED-AMT > FIO-ALLOWED-AMT
               MOVE FIO-ALLOWED-AMT TO CLM-APPROVED-AMT
           ELSE
               MOVE CLM-BILLED-AMT TO CLM-APPROVED-AMT
           END-IF

           SET CLAIM-APPROVED TO TRUE.

       3000-EXIT.
           EXIT.

🧪 The Human Factor: James Okafor points out that the hardest part of maintaining IMS programs is not the DL/I calls themselves — it is understanding the business logic encoded in the hierarchy. Why is CREDENTIAL a direct child of PROVIDER rather than a child of SPECIALTY? Because a provider's medical license covers all their specialties. Why is FEE-SCHEDULE under CONTRACT rather than under PROVIDER? Because fees vary by contract. The hierarchy is the business model. When you inherit an IMS program, study the DBD before you read a single line of COBOL.

31.11 IMS in Modern Architectures

IMS is not frozen in the 1960s. IBM has steadily modernized the platform to integrate with contemporary architectures.

31.11.1 IMS Connect

IMS Connect is a TCP/IP gateway that allows distributed applications to access IMS transactions and databases. A Java application running on a cloud server can call an IMS transaction through IMS Connect, receive the response, and display it in a web browser — all without the end user knowing they are interacting with a hierarchical database.

[Web Browser] → [REST API] → [IMS Connect] → [IMS MPP] → [IMS Database]

31.11.2 IMS Open Database

IMS Open Database allows distributed applications to issue DL/I-like calls over TCP/IP without going through the traditional IMS message processing infrastructure. Java and .NET programs can directly query IMS databases.

31.11.3 IMS and z/OS Connect EE

z/OS Connect Enterprise Edition can expose IMS transactions as RESTful APIs with automatic JSON/COBOL copybook mapping. This is how many organizations modernize their IMS investments:

[Mobile App] → [API Gateway] → [z/OS Connect EE] → [IMS Transaction] → [IMS DB]

The COBOL program inside IMS does not change at all. The new technology wraps around it, providing a modern interface.

31.11.4 Java Access to IMS

IBM provides the IMS Universal JDBC driver, which allows Java programs to access IMS databases using SQL-like syntax that gets translated to DL/I calls:

// Java accessing IMS via Universal JDBC driver
Connection conn = DriverManager.getConnection(
    "jdbc:ims://mainframe:8888/IMS1", props);
PreparedStatement ps = conn.prepareStatement(
    "SELECT * FROM PCB01.CUSTOMER WHERE CUST_ID = ?");
ps.setString(1, "C000010001");
ResultSet rs = ps.executeQuery();

The SQL gets translated to DL/I calls under the covers. The IMS database structure has not changed — only the access method has been modernized.

💡 The Modernization Spectrum: IMS modernization illustrates the full range of approaches: from wrapping (IMS Connect), to bridging (JDBC driver), to rewriting (migrating data to DB2). Most organizations use a combination. The COBOL programs accessing IMS databases are often the most stable, performant components in the architecture — the modernization happens in the layers around them.

31.12 Performance Considerations

IMS's hierarchical model has inherent performance characteristics that differ from relational databases:

Strengths: - Parent-child traversal is blazingly fast — following pointers, no joins needed - Predictable performance — the path through the hierarchy determines I/O count - Root segment access — HDAM provides hash-based O(1) access to root segments - Minimal overhead — no query optimizer, no execution plan; the program controls navigation

Weaknesses: - Cross-hierarchy queries are expensive — "find all accounts with balance > $100K" requires scanning the entire database - Insert performance varies — inserting into a database with many pointer chains requires pointer updates - Schema changes are disruptive — adding a new segment type or changing the hierarchy requires database reorganization

Optimization tips: 1. Always use the most specific SSAs possible — let IMS navigate directly rather than scanning 2. Use GU for direct access, GN/GNP only when you need sequential processing 3. Use the D command code to retrieve multiple segments in one call when you need a full path 4. Minimize the number of DL/I calls — each call has overhead 5. Use HDAM for databases where you primarily access root segments by key 6. Use HIDAM when you need both direct access and sequential processing

📊 Performance Rule of Thumb: In IMS, the number of DL/I calls is the primary performance driver. A program that makes 100 DL/I calls to process a transaction will generally be 10x slower than one that achieves the same result in 10 calls. Experienced IMS programmers like Maria Chen obsessively minimize call counts.

31.13 Common IMS Programming Patterns

31.13.1 The Position-and-Process Pattern

The most common pattern: position on a parent with GU, then process children with GNP:

       PERFORM VARYING WS-IDX FROM 1 BY 1
                   UNTIL WS-IDX > WS-CUST-COUNT
           MOVE WS-CUST-TABLE(WS-IDX) TO SSA-CUST-ID

           CALL 'CBLTDLI' USING DLI-GU
                                 CUSTOMER-PCB
                                 CUSTOMER-IO-AREA
                                 CUSTOMER-QUAL-SSA

           IF PCB-STATUS-CODE = SPACES
               SET MORE-CHILDREN TO TRUE
               PERFORM UNTIL NO-MORE-CHILDREN
                   CALL 'CBLTDLI' USING DLI-GNP
                                         CUSTOMER-PCB
                                         ACCOUNT-IO-AREA
                                         ACCOUNT-UNQUAL-SSA
                   EVALUATE PCB-STATUS-CODE
                       WHEN SPACES
                           PERFORM PROCESS-ACCOUNT
                       WHEN 'GE'
                           SET NO-MORE-CHILDREN TO TRUE
                       WHEN OTHER
                           PERFORM ABEND-HANDLER
                   END-EVALUATE
               END-PERFORM
           END-IF
       END-PERFORM.

31.13.2 The Sequential Scan Pattern

Processing all database records sequentially (for reporting or batch updates):

       CALL 'CBLTDLI' USING DLI-GN
                             CUSTOMER-PCB
                             CUSTOMER-IO-AREA
                             CUSTOMER-UNQUAL-SSA.

       PERFORM UNTIL PCB-STATUS-CODE = 'GB'
           PERFORM PROCESS-CUSTOMER-RECORD
           CALL 'CBLTDLI' USING DLI-GN
                                 CUSTOMER-PCB
                                 CUSTOMER-IO-AREA
                                 CUSTOMER-UNQUAL-SSA
       END-PERFORM.

Note: Status code 'GB' (or 'GE' at the root level) indicates end of database.

31.13.3 The Update-in-Place Pattern

Retrieve with hold, modify, replace:

       CALL 'CBLTDLI' USING DLI-GHU
                             CUSTOMER-PCB
                             ACCOUNT-IO-AREA
                             CUSTOMER-QUAL-SSA
                             ACCOUNT-QUAL-SSA.

       IF PCB-STATUS-CODE = SPACES
           ADD WS-DEPOSIT-AMT TO AIO-ACCT-BAL
           MOVE FUNCTION CURRENT-DATE(1:8)
               TO AIO-LAST-UPDATE

           CALL 'CBLTDLI' USING DLI-REPL
                                 CUSTOMER-PCB
                                 ACCOUNT-IO-AREA

           IF PCB-STATUS-CODE NOT = SPACES
               DISPLAY 'REPL FAILED: ' PCB-STATUS-CODE
               PERFORM ABEND-HANDLER
           END-IF
       END-IF.

31.13.4 The Insert-Under-Parent Pattern

Position on the parent (or use qualified SSAs), then insert:

      *--- Build the new transaction segment ---*
       MOVE WS-TRANS-DATE    TO TIO-TRANS-DATE
       MOVE WS-TRANS-AMT     TO TIO-TRANS-AMT
       MOVE WS-TRANS-TYPE    TO TIO-TRANS-TYPE
       MOVE WS-TRANS-DESC    TO TIO-TRANS-DESC

      *--- Insert under the correct account ---*
       CALL 'CBLTDLI' USING DLI-ISRT
                             CUSTOMER-PCB
                             TRANSACTION-IO-AREA
                             CUSTOMER-QUAL-SSA
                             ACCOUNT-QUAL-SSA
                             TRANS-UNQUAL-SSA.

       EVALUATE PCB-STATUS-CODE
           WHEN SPACES
               ADD 1 TO WS-INSERT-COUNT
           WHEN 'II'
               DISPLAY 'DUPLICATE TRANSACTION — SKIPPED'
           WHEN OTHER
               DISPLAY 'ISRT FAILED: ' PCB-STATUS-CODE
               PERFORM ABEND-HANDLER
       END-EVALUATE.

31.14 IMS vs. DB2 — When to Use Which

At GlobalBank, Priya Kapoor often fields the question: "Why don't we just migrate everything to DB2?" Her answer is nuanced:

Factor IMS Advantage DB2 Advantage
Hierarchical data Natural fit Requires JOINs or recursive CTEs
Ad hoc queries Poor Excellent (SQL)
Transaction throughput Superior for predefined paths Very good, but more overhead
Developer availability Shrinking pool Larger pool
Schema flexibility Rigid Flexible (ALTER TABLE)
Reporting Must write programs SQL queries, BI tools
Integration IMS Connect, JDBC Native JDBC, ODBC, REST
Recovery Proven, 50+ years Proven, 40+ years

The pragmatic answer: use both. Many organizations run IMS for high-volume, predefined-path transaction processing and DB2 for reporting, ad hoc queries, and new applications. The data is synchronized through extract processes, IMS-to-DB2 replication, or application-level dual-write patterns.

⚖️ The Modernization Spectrum in Practice: GlobalBank's strategy, designed by Priya Kapoor, is: 1. Keep in IMS: High-volume core transactions (account posting, balance inquiry) 2. Mirror to DB2: Customer data replicated to DB2 for reporting and analytics 3. New development in DB2: New products and features use DB2 from the start 4. Wrap with APIs: IMS transactions exposed via z/OS Connect for mobile/web access

This is the modernization spectrum in action — not a binary "keep or replace" but a gradient of strategies applied based on business need.

31.15 Checkpoint and Restart

Batch DL/I programs that process large volumes of data should take periodic checkpoints. A checkpoint commits all database changes, writes a restart record, and allows the program to restart from the checkpoint if it abends.

       01  WS-CHKP-AREA.
           05  CHKP-RECORD-COUNT PIC 9(10).
           05  CHKP-TOTAL-AMT    PIC S9(13)V99.
           05  CHKP-LAST-KEY     PIC X(10).
           05  CHKP-TIMESTAMP    PIC X(26).

       ...

       5000-CHECKPOINT.
           MOVE WS-RECORD-COUNT TO CHKP-RECORD-COUNT
           MOVE WS-TOTAL-AMT    TO CHKP-TOTAL-AMT
           MOVE WS-LAST-CUST-ID TO CHKP-LAST-KEY
           MOVE FUNCTION CURRENT-DATE TO CHKP-TIMESTAMP

           CALL 'CBLTDLI' USING DLI-CHKP
                                 IO-PCB
                                 WS-CHKP-AREA.

           IF PCB-STATUS-CODE NOT = SPACES
               DISPLAY 'CHECKPOINT FAILED: ' PCB-STATUS-CODE
               PERFORM 9999-ABEND
           END-IF

           DISPLAY 'CHECKPOINT AT RECORD: '
                   CHKP-RECORD-COUNT.

For restart, your program checks for a restart area at the beginning:

       1000-INITIALIZE.
           CALL 'CBLTDLI' USING DLI-XRST
                                 IO-PCB
                                 WS-CHKP-AREA.

           IF PCB-STATUS-CODE = SPACES
      *        Normal start — no restart data
               MOVE ZEROS TO WS-RECORD-COUNT
               MOVE ZEROS TO WS-TOTAL-AMT
           ELSE
      *        Restarting — restore saved state
               MOVE CHKP-RECORD-COUNT TO WS-RECORD-COUNT
               MOVE CHKP-TOTAL-AMT TO WS-TOTAL-AMT
               MOVE CHKP-LAST-KEY TO WS-LAST-CUST-ID
               DISPLAY 'RESTARTING FROM RECORD: '
                       WS-RECORD-COUNT
           END-IF.

31.16 Debugging IMS Programs

Debugging IMS programs adds a layer of complexity beyond standard COBOL debugging:

  1. Always display the status code and segment name after DL/I calls during development:
       IF WS-DEBUG-MODE = 'Y'
           DISPLAY 'DLI CALL: GU CUSTOMER'
           DISPLAY '  STATUS: ' PCB-STATUS-CODE
           DISPLAY '  SEGMENT: ' PCB-SEG-NAME
           DISPLAY '  KEY FB: ' PCB-KEY-FEEDBACK
       END-IF.
  1. Check the key feedback area — it tells you where IMS is positioned in the hierarchy, which is invaluable when GN calls return unexpected segments.

  2. Common IMS abend codes: - U0457 — PSB not found or not authorized - U0474 — Database not available - U0778 — DL/I call error (often a bad SSA format) - U3303 — IMS buffer pool full

  3. Use DFSDDLT0 — IBM's DL/I test program — to test DL/I calls interactively before coding them in COBOL.

⚠️ Defensive Programming: The most dangerous IMS bug is continuing to process after a failed DL/I call. When a GN or GNP returns GE, the I/O area still contains the data from the previous successful call. If you do not check the status code, you will process the same segment twice — or worse, update the wrong record. Maria Chen's rule: "Every DL/I call gets an EVALUATE. No exceptions."

31.17 Secondary Indexes in IMS

One of the limitations of a pure hierarchical model is that you can only access root segments by their primary key (or by sequential scan). What if you need to find all customers by last name, or all accounts by account type? IMS addresses this with secondary indexes.

31.17.1 What is a Secondary Index?

A secondary index is a separate IMS database that provides an alternative access path to segments in the primary database. It is conceptually similar to a secondary index in DB2, though the implementation is quite different.

Consider GlobalBank's customer database. The primary access path is by CUST-ID (the root segment key). But customer service representatives often need to look up customers by Social Security Number or by name. Without a secondary index, the only option would be to scan all 3.2 million customer records.

A secondary index on CUST-SSN creates a separate database that maps SSN values to pointers into the primary customer database. When a program issues a GU with an SSA on the indexed field, IMS automatically uses the secondary index to navigate directly to the correct record.

31.17.2 Defining a Secondary Index

The DBA creates a secondary index in the DBD:

         LCHILD NAME=(XSSN,CUSTXDB),POINTER=INDX
         XDFLD  NAME=CUST-SSN,SRCH=CUST-SSN,
                SEGMENT=CUSTOMER

The XDFLD (Index Source Field) defines: - Which field in the primary database provides the index key - Which segment contains that field - The name of the index database

31.17.3 Programming with Secondary Indexes

From the COBOL programmer's perspective, secondary indexes are mostly transparent. Your PSB must specify that it uses the secondary index, and your SSA references the index field name. The DL/I call looks the same:

       01  CUSTOMER-SSN-SSA.
           05  FILLER          PIC X(9) VALUE 'CUSTOMER('.
           05  FILLER          PIC X(10) VALUE 'CUST-SSN ='.
           05  SSA-CUST-SSN   PIC X(9).
           05  FILLER          PIC X(1) VALUE ')'.

      *--- Retrieve customer by SSN via secondary index ---*
       MOVE '123456789' TO SSA-CUST-SSN

       CALL 'CBLTDLI' USING DLI-GU
                             CUSTOMER-PCB
                             CUSTOMER-IO-AREA
                             CUSTOMER-SSN-SSA.

IMS automatically routes this call through the secondary index. The programmer does not need to know whether the access path uses the primary key or a secondary index — the DL/I call interface is the same.

31.17.4 Trade-offs of Secondary Indexes

Secondary indexes are powerful but come with costs:

Benefits: - Enable direct access by non-key fields - Eliminate full database scans for alternate lookups - Transparent to the application program (via PSB configuration)

Costs: - Additional I/O overhead on every insert, update, and delete (the index must be maintained) - Additional disk space for the index database - Additional logging and recovery overhead - Index database must be reorganized periodically

At GlobalBank, Priya Kapoor restricts secondary indexes to fields with high query frequency and high selectivity. The customer SSN index is justified because it is queried thousands of times per day and each SSN maps to exactly one customer. A secondary index on CUST-TYPE (which has only four values across 3.2 million customers) would be a poor choice — the index would not reduce the search space enough to justify the maintenance overhead.

31.18 Logical Relationships in Depth

We briefly mentioned logical databases in Section 31.2.3. Let us explore them further, as they are a key feature that distinguishes IMS from simpler hierarchical storage systems.

31.18.1 The Problem Logical Relationships Solve

Consider two physical databases at GlobalBank:

Customer Database:           Product Database:
CUSTOMER                     PRODUCT
├── ACCOUNT                  ├── RATE
└── ADDRESS                  └── TERM

When a customer opens an account, that account is tied to a product (e.g., "Premium Savings" or "Standard Checking"). In a relational database, you would use a foreign key from ACCOUNT to PRODUCT. In IMS, the equivalent is a logical relationship.

A logical relationship creates a virtual parent-child connection between segments in different physical databases. You can then create a logical database that presents a merged view:

Logical Database View:
CUSTOMER
├── ACCOUNT
│   └── PRODUCT (virtual child from Product Database)
│       ├── RATE
│       └── TERM
└── ADDRESS

A program using this logical view can navigate from an account directly to its product details without knowing they reside in separate physical databases.

31.18.2 Types of Logical Relationships

IMS supports three types of logical relationships:

  1. Unidirectional: Navigation goes one way only — from the logical child to the logical parent. You can go from ACCOUNT to PRODUCT, but not from PRODUCT to ACCOUNT.

  2. Bidirectional physically paired: Navigation goes both ways, using physical pointers stored in both databases. More storage overhead but supports both access directions.

  3. Bidirectional virtually paired: Navigation goes both ways, but only one direction uses physical pointers. The other direction is resolved at runtime by IMS.

31.18.3 Programming Considerations

When programming against a logical database, the DL/I calls are identical to programming against a physical database. The key differences are:

  • Your PSB references the logical database definition
  • Sensitivity includes segments from both physical databases
  • Insert and delete operations may have additional rules (e.g., you may not be able to delete a segment that participates in a logical relationship)
  • Performance characteristics differ because logical navigation may require cross-database I/O
      *--- In a logical database view, you can navigate
      *--- seamlessly across physical database boundaries ---*
       CALL 'CBLTDLI' USING DLI-GU
                             LOGICAL-PCB
                             PRODUCT-IO-AREA
                             CUSTOMER-QUAL-SSA
                             ACCOUNT-QUAL-SSA
                             PRODUCT-UNQUAL-SSA.

This single call navigates from CUSTOMER to ACCOUNT (in the customer database) and then to PRODUCT (in the product database) — all transparently.

⚠️ Complexity Warning: Logical relationships add significant complexity to database administration. DBAs must manage pointer rebuilds, cross-database recovery, and reorganization coordination. As a COBOL programmer, you benefit from the seamless navigation, but you should understand that the DBA is doing considerable work behind the scenes to make it possible.

31.19 IMS and the COBOL Copybook

In production IMS shops, the segment I/O areas are typically defined in copybooks (maintained by the DBA or data administration team) rather than coded directly in each program. This ensures consistency:

      *--- In your program's WORKING-STORAGE ---*
       COPY CUSTSEGS.

Where CUSTSEGS contains:

      *================================================================*
      * COPYBOOK: CUSTSEGS                                             *
      * PURPOSE:  Segment I/O areas for CUSTDB (Customer Database)     *
      * DBD:      CUSTDB                                               *
      * UPDATED:  2025-03-15 by DBA Team                               *
      *================================================================*
       01  CUSTOMER-IO-AREA.
           05  CIO-CUST-ID      PIC X(10).
           05  CIO-CUST-NAME    PIC X(40).
           05  CIO-CUST-SSN     PIC X(9).
           05  CIO-CUST-DOB     PIC X(10).
           05  CIO-CUST-TYPE    PIC X(2).
           05  CIO-CUST-STATUS  PIC X(1).
           05  CIO-CUST-PHONE   PIC X(15).
           05  CIO-CUST-EMAIL   PIC X(50).
           05  FILLER            PIC X(113).

       01  ACCOUNT-IO-AREA.
           05  AIO-ACCT-NUM     PIC X(12).
           05  AIO-ACCT-TYPE    PIC X(10).
           05  AIO-ACCT-BAL     PIC S9(11)V99 COMP-3.
           05  AIO-OPEN-DATE    PIC X(10).
           05  AIO-ACCT-STATUS  PIC X(1).
           05  AIO-LAST-UPDATE  PIC X(26).
           05  AIO-INTEREST-RATE PIC V9(4) COMP-3.
           05  FILLER            PIC X(81).

Similarly, SSA definitions and PCB masks should be standardized in copybooks. This practice avoids the common bug where a programmer's SSA field positions do not match the actual DBD definitions.

💡 Best Practice: At GlobalBank, Maria Chen requires that all IMS programs use three standard copybooks: one for segment I/O areas (CUSTSEGS), one for SSA definitions (CUSTSSAS), and one for PCB masks (CUSTPCBS). Any change to the database structure triggers a copybook update, and all programs that COPY the affected copybooks are recompiled. This is the IMS equivalent of maintaining schema consistency in a relational system.

31.20 Try It Yourself: Building a Complete IMS Application

This extended exercise walks you through building a complete mini-application in your Student Mainframe Lab.

Step 1: Design the Hierarchy

Design a simple IMS hierarchy for a library system:

LIBRARY (root) — one per branch
├── BOOK
│   ├── COPY (physical copies of the book)
│   └── RESERVATION
└── MEMBER
    └── CHECKOUT

Step 2: Define the Segments

Define the segment layouts for each segment type. For example:

  • LIBRARY: LIB-ID (key, 5 bytes), LIB-NAME (30 bytes), LIB-ADDRESS (50 bytes)
  • BOOK: BOOK-ISBN (key, 13 bytes), BOOK-TITLE (50 bytes), BOOK-AUTHOR (40 bytes)
  • COPY: COPY-NUM (key, 5 bytes), COPY-STATUS (1 byte: A=available, O=out, L=lost)
  • MEMBER: MEM-ID (key, 8 bytes), MEM-NAME (40 bytes), MEM-PHONE (15 bytes)
  • CHECKOUT: CHK-DATE (key, 10 bytes), CHK-DUE-DATE (10 bytes), CHK-BOOK-ISBN (13 bytes)
  • RESERVATION: RES-MEM-ID (key, 8 bytes), RES-DATE (10 bytes)

Step 3: Write the Programs

Write three programs:

  1. LIBINQ01 — Library inquiry: Accept a library ID, display all books and their copy status
  2. LIBCHK01 — Book checkout: Accept a member ID and book ISBN, create a CHECKOUT record, update COPY status to 'O'
  3. LIBRPT01 — Overdue report: Scan all CHECKOUT records, list those where CHK-DUE-DATE < today

Step 4: Test

If you have access to IMS (via IBM Z Xplore or a local installation), create the database and run your programs. If you are using GnuCOBOL without IMS, simulate the DL/I calls using indexed files — the programming patterns remain the same.

This exercise gives you hands-on experience with all the key concepts from this chapter: hierarchical design, DL/I calls, SSAs, status code handling, and common programming patterns.

31.21 Common IMS Migration Pitfalls

When organizations consider migrating from IMS to a relational database, several common pitfalls arise that developers should understand:

31.21.1 The Denormalization Trap

IMS hierarchies are inherently denormalized. A customer's account balance, for example, might be stored directly in the ACCOUNT segment rather than calculated from transaction records. When migrating to DB2, the temptation is to normalize everything into Third Normal Form. But this can dramatically change the performance characteristics of existing programs.

Consider a program that currently makes one GU call to retrieve an account balance. If that balance is now derived from a SUM of transactions in DB2, the single DL/I call becomes a potentially expensive aggregation query. Priya Kapoor's rule: "Migrate the data model, but don't assume you need to normalize it. Match the current performance first, then refine."

31.21.2 The Pointer Dependency Problem

IMS programs sometimes depend on the physical ordering of segments within the hierarchy. A GN call returns segments in hierarchical sequence, which is a defined order based on the key fields and the hierarchy structure. In DB2, the equivalent ORDER BY clause must be explicitly specified. Programs that rely on implicit ordering can produce subtly wrong results after migration if the ORDER BY is not precisely correct.

31.21.3 The Batch Window Problem

IMS batch programs often achieve their performance through the navigational access pattern — following pointers is extremely fast. The equivalent DB2 program, using SQL with JOINs and aggregations, may be significantly slower. If the IMS batch completes in 2 hours and the DB2 equivalent takes 6 hours, the batch window is broken. This is the #1 technical reason migrations are abandoned or delayed.

31.21.4 The Parallel Access Problem

IMS supports sophisticated locking and sharing mechanisms (PI — Program Isolation, and block-level data sharing) that allow many programs to access the same database concurrently. Migrating to DB2 requires careful attention to isolation levels, lock granularity, and deadlock prevention to achieve the same concurrency characteristics.

💡 Legacy != Obsolete — The Chapter's Core Theme: This chapter has shown that IMS is not a relic to be discarded at the first opportunity. It is a powerful, high-performance database system with specific strengths — hierarchical traversal, predictable performance, massive throughput — that modern alternatives do not always match. The wise approach is to understand IMS thoroughly, use it where it excels, modernize the access layers around it, and migrate selectively based on business need rather than fashion.

31.22 Chapter Summary

IMS/DB is a hierarchical database management system that has been in continuous production use since 1968. In this chapter, you learned:

  • Hierarchical model: Data is organized as trees of segments, with parent-child relationships defining the structure
  • DL/I calls: GU, GN, GNP for retrieval; GHU, GHN, GHNP for hold-retrieval; ISRT, REPL, DLET for updates
  • PCBs and PSBs: Your program's interface to IMS — the PCB reports status codes and current position; the PSB defines which databases and segments you can access
  • SSAs: Segment Search Arguments control navigation — unqualified for "any," qualified for specific criteria, with command codes for advanced behavior
  • Status codes: Spaces for success, GE for not found, and many others — always check after every call
  • Region types: Batch DL/I, BMP, MPP, and Fast Path — each suited to different workload patterns
  • Modern integration: IMS Connect, Open Database, z/OS Connect EE, and JDBC provide modern access methods

IMS is not a relic of the past — it is a proven, high-performance database system that continues to evolve. Understanding IMS is essential for any COBOL developer who works with enterprise systems, because the chances of encountering IMS data are high. The hierarchical model may feel unfamiliar after years of relational thinking, but its navigational approach offers unmatched performance for predefined access paths.

In the next chapter, we will explore transaction design patterns — how to ensure that your database updates (whether IMS, DB2, or VSAM) are atomic, consistent, isolated, and durable.


"IMS is the quiet giant of enterprise computing. It doesn't make the headlines, but it processes the transactions that keep the world's economy running." — Maria Chen, during Derek Washington's first IMS training session