こんにちは。長年、金融や流通の基幹システムでメインフレームの伴走をしてきたシステムアーキテクトの私だ。
君たちが日々向き合っているPL/I(Programming Language One)とCICS(Customer Information Control System)の組み合わせは、まさに日本の社会インフラを支える心臓部そのものだ。画面からの入力やVSAM(KSDSなど)のレコードを、CICSの通信エリアやストレージからPL/Iの構造体にいかに正確にマッピングするか。この基本を疎かにすると、突発的なASRA(異常終了)や、原因特定に何日も費やすデータ化けの悪夢にうなされることになる。
今日は、「EXEC CICS READ/WRITEコマンドとPL/I構造体のマッピング」という、実務で最も重要かつ、一歩間違えるとシステムを沈没させる地雷原について、私の経験を踏まえて徹底的に解説しよう。
—
1. 予約語を持たないPL/IとCICSマッピングの罠
まず、PL/Iの最大の特徴であり、CICSプログラミングにおいて諸刃の剣となる仕様について触れておこう。
君たちも知っている通り、PL/Iには「厳密な予約語(Reserved Words)」という概念が存在しない。 `IF` や `READ`、`WRITE` でさえも、文脈によってはただの変数名として定義できてしまう言語仕様になっている。
これがCICSのプリプロセッサ(DFHEDLIなど)とどう絡むか。
CICSコマンド(`EXEC CICS READ …` など)を記述すると、プリプロセッサがこれをホスト言語のCALL文や構造体参照に展開する。もし、君がうっかりCICSが内部で使用するキーワードや、BMS(Basic Mapping Support)のマップ名と重複するような識別子を構造体内に定義してしまったらどうなるか。コンパイラは文脈から判断しようとして、予期せぬ構文エラーを吐くか、最悪の場合、コンパイルは通るのに実行時にストレージを破壊するコードが生成される。
実務のコーディング標準において、CICS通信領域(DFHCOMWAなど)やVSAMレコードをマッピングする構造体では、フィールド名にプレフィックス(例: `WS-`, `IO-`, `CICS-` など)を必ず付与すること。これが、言語仕様の隙間を塞ぐための第一の防衛線だ。
—
2. アライメント(境界調整)の恐怖と `ALIGNED` / `UNALIGNED`
CICSのストレージ(`GETMAIN` で取得した領域や、ファイルから読み込んだレコードイメージ)は、基本的にバイトストリーム(隙間なく連続したデータ)として渡される。
一方、PL/Iのデフォルトのデータ構造はどうなっているか。
君たちが普段何気なく書いている構造体は、コンパイラがハードウェア(System zのアーキテクチャ)にとってアクセス効率が良いように、勝手に境界調整(アライメント)を行う。
- `FIXED BINARY(31)`(4バイト整数)は4の倍数のアドレスに配置される。
- 構造体の中に、奇数バイトの文字項目を挟んだりすると、コンパイラが勝手にパディング(埋め草の空白バイト)を挿入する。
この「勝手なパディング」が、CICSのレコードレイアウトや電文フォーマット(Copybook / 構造体)と激突する。CICS側から渡された生データをそのままPL/Iのデフォルト構造体に放り込むと、パディングの分だけオフセットがズレ、後ろの項目がすべてゴミデータに化けるか、領域外参照で即死する。
対策:必ず `UNALIGNED` を指定せよ
CICSのマップや外部ファイル、通信エリアとデータをやり取りするすべての構造体(あるいはレベル1のグループ)には、必ず `UNALIGNED` 属性を明示しなければならない。
1
/ 正しいCICS通信エリアのマッピング例 /
DCL 1 CICS-COMM-AREA UNALIGNED,
5 CCA-TRAN-ID CHAR(4), / トランザクションID /
5 CCA-RC FIXED BIN(15), / 復帰コード (2バイト) /
5 CCA-MSG-AREA CHAR(78); / メッセージ領域 /
この `UNALIGNED` を忘れるだけで、テスト環境では動いたのに本番の本番データ(特にバイナリ項目の混じった電文)で一発レッドカードを食らうことになる。絶対に忘れないでほしい。
—
3. 実践:EXEC CICS READ/WRITE と PL/I構造体の統合コード
百聞は一見にしかずだ。VSAM(KSDS)のレコードをCICS経由で読み込み、内容を加工して書き戻す(あるいは別ファイルに書き出す)典型的なオンラインプログラムの断片を示そう。大文字ベースの記述、適切なインデント、そしてPL/Iの `BUILTIN` 関数(`LENGTH` など)を活用した実用的なコードだ。
1
/—————————————————————–/
/ プログラム名: CUSTUPD – 顧客マスタ更新オンラインサブルーチン /
/—————————————————————–/
CUSTUPD: PROC(DFHCOMWA) OPTIONS(MAIN REENTRANT);
/ 通信エリアの定義(UNALIGNED必須) /
Dcl 1 DFHCOMWA UNALIGNED,
5 CW-FUNC-CD CHAR(1), / 処理機能 (R:読込/U:更新) /
5 CW-CUST-ID CHAR(8), / 顧客ID /
5 CW-RET-MSG CHAR(50); / 応答メッセージ /
/ 顧客マスタVSAMレコード構造体(ストレージ整合性を維持) /
Dcl 1 CUST-REC UNALIGNED,
5 CR-KEY CHAR(8), / 顧客ID (キー) /
5 CR-NAME CHAR(30), / 顧客名 /
5 CR-STATUS CHAR(2), / ステータス /
5 CR-UPD-DATE FIXED DEC(7,0), / 更新日 (CCYYMMD) /
5 CR-FILLER CHAR(10); / 予備領域 /
/ ワーク変数 /
Dcl WS-LENGTH FIXED BIN(15) INIT(0);
————————————————————-
/ 処理分岐 /
————————————————————-
SELECT (CW-FUNC-CD);
WHEN (‘R’) DO;
CALL READ_CUSTOMER_RECORD;
END;
WHEN (‘U’) DO;
CALL UPDATE_CUSTOMER_RECORD;
END;
OTHERWISE
CW-RET-MSG = ‘INVALID FUNCTION CODE SPECIFIED.’;
END;
EXEC CICS RETURN;
————————————————————-
/ 内部手続き: 顧客レコード読み込み (EXEC CICS READ) /
————————————————————-
READ_CUSTOMER_RECORD: PROC;
/ レコード長をBUILTIN関数のLENGTHで動的かつ安全に取得 /
WS-LENGTH = LENGTH(CUST-REC);
EXEC CICS READ
DATASET(‘CUSTFILE’)
INTO(CUST-REC)
LENGTH(WS-LENGTH)
RIDFLD(CW-CUST-ID)
KEYLENGTH(8)
RESP(WS-RESP);
SELECT (WS-RESP);
WHEN (DFHRESP(NORMAL))
CW-RET-MSG = ‘READ SUCCESSFUL.’;
WHEN (DFHRESP(NOTFND))
CW-RET-MSG = ‘CUSTOMER NOT FOUND.’;
OTHERWISE
CALL HANDLE_ABEND;
END;
END READ_CUSTOMER_RECORD;
————————————————————-
/ 内部手続き: 顧客レコード更新 (EXEC CICS REWRITE) /
————————————————————-
UPDATE_CUSTOMER_RECORD: PROC;
/ あらかじめREADで取得済みの前提での更新ロジック /
WS-LENGTH = LENGTH(CUST-REC);
/ サンプルとしてステータスを変更 /
CR-STATUS = ’02’;
CR-UPD-DATE = 20260330;
EXEC CICS REWRITE
DATASET(‘CUSTFILE’)
FROM(CUST-REC)
LENGTH(WS-LENGTH)
RESP(WS-RESP);
IF WS-RESP = DFHRESP(NORMAL) THEN
CW-RET-MSG = ‘UPDATE COMPLETED.’;
ELSE
CALL HANDLE_ABEND;
END UPDATE_CUSTOMER_RECORD;
————————————————————-
/ 異常系ハンドリング用ダミー /
————————————————————-
HANDLE_ABEND: PROC;
CW-RET-MSG = ‘CICS SYSTEM ERROR OCCURRED.’;
/ 本来はここでABCODEを指定してEXEC CICS ABENDを呼ぶ /
END HANDLE_ABEND;
END CUSTUPD;
—
4. ベテランからの現場の教訓(デバッグのコツ)
最後に、私が幾多の障害現場で血を流しながら得た教訓を授けよう。
1. `LENGTH` ビルトイン関数を過信せず、しかし手動計算はするな
先ほどのコードでも `LENGTH(CUST-REC)` を使っている。これはコンパイル時に構造体のサイズを正確に計算してくれるため、手動でマジックナンバー(例: `60` など)を書くよりも圧倒的に安全だ。ただし、ポインタを介したベース付変数(Based変数)を扱う場合は、ポインタ自体のサイズではなく参照先のサイズが正しく取れているか、CICSの `LENGTH` オプションに渡す値がバイト数と一致しているかを必ずスナップショットやトレースで確認しろ。
2. ストレージ・バイオレーションの検知
もしCICS環境で `ASRA` や `AEIV` などのアブコードに遭遇したら、真っ先に疑うべきは「構造体のレイアウト不一致」と「領域のはみ出し(Subscript Range / String Range Over)」だ。PL/Iコンパイルオプションで `STG(CEE)` や `CHECK(SUBRG, ONCHAR)` を有効にしておき、開発段階で境界違反を検知できるようにしておくこと。本番稼働後にこれが起きると、他のトランザクションのメモリを巻き込んで原因追跡が極めて困難になる。
基幹システムのモダナイゼーションやクラウド移行が叫ばれる昨今だが、動いているメインフレームの根幹にあるデータ構造のルールが変わるわけではない。PL/Iの柔軟性とCICSの厳密なストレージ管理の境界線を正しく理解し、型破りな言語仕様に足元をすくわれないよう、堅牢なコードを書き続けてほしい。期待しているぞ。
