Skip to content
PYD ENTERPRISES

Data stores anti-pattern

VSAM alternate index coupling

Programs reach a KSDS through alternate index paths defined outside the code, in IDCAMS jobs and JCL, so access patterns, duplicate handling and index maintenance are invisible in the COBOL source.

FAMILY
Data stores
CUTOVER RISK
High
Hides business logic or coupling that a rewrite will drop unless it is extracted explicitly.
MODERN MAPPING
Relational table with secondary index and explicit queries
GENERATED ARTIFACT
openapi.yaml

Before and after

legacy source → modern mapping

Legacy: exhibits the pattern

EXPOSURE.cblCOBOL
1 SELECT ACCT-MASTER ASSIGN TO ACCTMST
2 ORGANIZATION IS INDEXED
3 ACCESS MODE IS DYNAMIC
4 RECORD KEY IS ACCT-NO
5 ALTERNATE RECORD KEY IS ACCT-TAX-ID
6 WITH DUPLICATES
7 FILE STATUS IS WS-FS.
8 ...
9 MOVE IN-TAX-ID TO ACCT-TAX-ID
10 START ACCT-MASTER KEY IS EQUAL TO ACCT-TAX-ID
11 INVALID KEY SET WS-DONE TO TRUE
12 END-START
13 PERFORM UNTIL WS-DONE
14 READ ACCT-MASTER NEXT RECORD
15 AT END SET WS-DONE TO TRUE
16 END-READ
17 IF NOT WS-DONE
18 IF ACCT-TAX-ID NOT = IN-TAX-ID
19 SET WS-DONE TO TRUE
20 ELSE
21 ADD ACCT-BALANCE TO WS-EXPOSURE
22 END-IF
23 END-IF
24 END-PERFORM.

Modern: Relational table with secondary index and explicit queries

account.sqlSQL
1CREATE TABLE account (
2 acct_no CHAR(10) PRIMARY KEY,
3 tax_id CHAR(9) NOT NULL,
4 balance DECIMAL(11,2) NOT NULL
5);
6
7-- was: AIX PROD.ACCTMST.TAXID.AIX (NONUNIQUEKEY)
8CREATE INDEX ix_account_tax_id ON account (tax_id);
9
10-- access pattern AP-07: total exposure by tax id
11SELECT COALESCE(SUM(balance), 0) AS exposure
12FROM account
13WHERE tax_id = :taxId;

Why it is dangerous in modernization

A VSAM key-sequenced data set has one prime key. Every other way of finding a record goes through an alternate index: a separate KSDS built by IDCAMS DEFINE ALTERNATEINDEX, linked to the base cluster by a PATH, and kept current only if it is in the upgrade set. Programs declare ALTERNATE RECORD KEY in the SELECT clause and loop over duplicates with START and READ NEXT. The real query (all accounts for one taxpayer, all claims for one provider) is spread across a SELECT clause, a START, a READ NEXT loop that must notice when the key changes, an IDCAMS job, and a DD statement whose name is derived from the base DD name. A data migration that copies records and recreates the prime key loses every one of those access paths, and any program behavior that depended on the order of duplicates.

How PYD ENTERPRISES detects it

PYD ENTERPRISES parses IDCAMS control statements alongside COBOL and JCL. DEFINE ALTERNATEINDEX, RELATE, KEYS, UNIQUEKEY or NONUNIQUEKEY, UPGRADE and DEFINE PATH become catalog entities linked to the base cluster. In COBOL, the SELECT clause ties a file to its prime and alternate keys, and dataflow analysis follows START and READ NEXT loops to find the key comparison that ends each one. Each loop is exported as a named access pattern: base cluster, alternate key, predicate, termination condition and the paragraph that consumes each record. AIXs that are not in the upgrade set, yet are read by online programs, are flagged as potentially stale.

Maps to: Relational table with secondary index and explicit queries

Each alternate index becomes a secondary index on the target table, and each access pattern becomes a named query or repository method with an explicit ORDER BY. The order of duplicates, which VSAM decided implicitly, becomes a decision someone makes and writes down. Upgrade-set behavior disappears, because the database maintains indexes transactionally.

Generated specification fragment

openapi.yamlYAML
1paths:
2 /parties/{taxId}/exposure:
3 get:
4 operationId: getExposureByTaxId
5 summary: Access pattern AP-07 (EXPOSURE.cbl)
6 parameters:
7 - name: taxId
8 in: path
9 required: true
10 schema: { type: string, pattern: '^[0-9]{9}$' }
11 responses:
12 '200':
13 content:
14 application/json:
15 schema:
16 $ref: '#/components/schemas/Exposure'
17 x-pyd-source: EXPOSURE.cbl:9-24

Every generated element links back to the source file and line range shown in the legacy pane. Review and sign off before building against it.

The definition lives in IDCAMS

The alternate index itself is defined in a utility job that may have run once, in the 1990s, and is kept only in a PDS member nobody opens. PYD ENTERPRISES reads those members as part of ingest.

DEFAIX.idcamsText
1 DEFINE ALTERNATEINDEX -
2 (NAME(PROD.ACCTMST.TAXID.AIX) -
3 RELATE(PROD.ACCTMST.KSDS) -
4 KEYS(9 40) -
5 NONUNIQUEKEY -
6 UPGRADE)
7 DEFINE PATH -
8 (NAME(PROD.ACCTMST.TAXID.PATH) -
9 PATHENTRY(PROD.ACCTMST.TAXID.AIX))
10 BLDINDEX INDATASET(PROD.ACCTMST.KSDS) -
11 OUTDATASET(PROD.ACCTMST.TAXID.AIX)

What the loop really asks

The COBOL loop in EXPOSURE.cbl is a SUM over all accounts sharing a tax ID. Nothing in the source says SUM, and the termination test (ACCT-TAX-ID NOT = IN-TAX-ID) is the only thing that keeps the loop from summing the whole rest of the file in alternate key order. Forgetting that comparison is a classic defect in hand-written COBOL, and when the engine finds a START and READ NEXT loop with no key-change test, it reports it: the legacy program may be computing something nobody intended.

Coupling you only see at runtime

  • In the JCL, the path is allocated on a DD whose name IBM COBOL derives from the base file's DD name with a sequence digit (ACCTMST1 for the first alternate key). Change one without the other and the program fails to open.
  • An AIX defined without UPGRADE is not maintained when the base cluster changes; it is rebuilt by a nightly BLDINDEX. Online reads through it see yesterday's data.
  • Several programs may share one path with different assumptions about duplicate order.

Each of these becomes a line item in the data migration plan, with the programs affected.

Find every VSAM alternate index coupling in your codebase.

Run PYD ENTERPRISES in your private VPC and get each occurrence with file, line range, modern mapping and equivalence fixtures, ready for your architects to review.