【入門編】EXEC CICS LINK/XCTLにおけるCOMMAREAの受け渡しとデータ構造の同期 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語の経験をお持ちの方にとって、IBMメインフレームの世界、そしてそこで動く「PL/I(ピーエルアイ)」という言語は、最初は少し独特で、要塞のように堅固に感じられるかもしれません。

特に、オンライン画面制御の王様である「CICS」と組み合わせたプログラム間通信、その中でもデータをやり取りする「COMMAREA(通信領域)」の扱いとなると、なんだか難しそうな呪文が並んでいるように見えて身構えてしまいますよね。

でも、安心してください。怖がる必要は全くありません。
今回は、PL/Iのユニークな命名規則の秘密に少しだけ触れつつ、CICSのトランザクション間でデータをバケツリレーするためのCOMMAREAの構造体同期と、現場で泣く泣くアベンド(異常終了)を踏まないためのコツを、一緒に優しく紐解いていきましょう!

—

1. そもそもPL/Iには「予約語」がない?(ちょっとしたトリビア)

JavaやCOBOLを触ってきた方なら、「この単語はキーワード(予約語)だから変数名に使っちゃいけないんだっけ?」と頭を悩ませた経験があるはずです。例えば、`IF`や`DATA`、`ID`といった言葉ですね。

ところが、PL/Iの面白い(そして少しお茶目な)ところは、言語としての厳密な「予約語」を持たないという設計思想にあります。

どういうことかと言うと、PL/Iコンパイラは、その単語が書かれた「文脈(コンテキスト)」を見て、「あ、ここで使われている `READ` は変数名だな」「こっちの `READ` はファイルを読み込む命令だな」と空気を読んで判断してくれます。

1
/ こんなアクロバティックな変数宣言も、PL/Iなら怒られません /
DCL READ FIXED BIN(31); / 「READ」という名前の数値変数 /
DCL IF CHAR(10); / 「IF」という名前の文字変数役 /

READ = 123; / 変数に値を代入している文脈 /

「じゃあ、なんでも自由気ままに名前をつけていいんだ!」と思ってしまいそうですが……ちょっと待ってくださいね。実務の現場では、コンパイラが許してくれても、人間(後からコードを読むあなたや同僚)が混乱して頭痛を起こしてしまいます。
ですから、常識的な範囲で意味の通る名前(識別子)をつけるのがプログラミングの美徳ですし、実際のマイグレーション現場でも厳格な命名規準が存在します。そのあたりは他の言語と変わりませんから、安心して普段通りの感覚で命名してくださいね。

—

2. CICSのCOMMAREAとPL/I構造体の関係

さて、本題の EXEC CICS LINK / XCTL における COMMAREA(通信領域)のお話です。

CICSのオンラインプログラム間では、共通のメモリー空間(COMMAREA)をバトンタッチしながら処理を進めます。Javaで言うところの「DTO(Data Transfer Object)」や、API通信の「JSON/XMLペイロード」のようなものだとイメージしてください。

ここで重要なのは、送信側のプログラムが作ったデータの並び順や長さ(メモリレイアウト)と、受信側のプログラムが解釈するデータのレイアウトが、1バイトのズレもなく完璧に一致していなければならないという点です。

もし、送信側が「最初の10バイトは顧客名です!」と言っているのに、受信側が「最初の5バイトは顧客名で、次の5バイトは……」と勝手に解釈をズラしてしまうと、文字化けを起こしたり、最悪の場合はメインフレーム特指しの非情なアベンド(例: `ASRA` などのストレージ違反)を引き起こしてしまいます。

これを防ぐための強力な武器が、PL/Iの構造体(STRUCTURE)定義です。

—

3. 実践!データ構造を完全に同期させるPL/Iコード例

百聞は一見に如かず。実際に、送信側プログラム(LINK元)と受信側プログラム(LINK先)で、COMMAREAの構造体をピタリと合わせるサンプルを見てみましょう。

送信側プログラム(親:MEMBERUP)の抜粋

1
/ ======================================================== /
/ 送信側プログラム: 会員情報更新トランザクション呼出 /
/ ======================================================== /
MBRUP: PROC OPTIONS(MAIN);

/ COMMAREAのレイアウト定義(送信側と全く同じにする!) /
DCL 1 CICS_COMMAREA,
3 CA_RET_CD CHAR(2), / 戻り値コード /
3 CA_CUST_ID CHAR(8), / 顧客ID /
3 CA_CUST_NAME CHAR(20), / 顧客名 /
3 CA_UPDATE_DAT FIXED DEC(7,0); / 更新日(0000000形式) /

/ ワーク変数の宣言 /
DCL W_CUST_ID CHAR(8) INIT(‘12345678’);

/ 1. 送信データを詰める /
CA_RET_CD = ‘ ‘;
CA_CUST_ID = W_CUST_ID;
CA_CUST_NAME = ‘山田 太郎’;
CA_UPDATE_DAT = 20231024;

/ 2. CICS LINKで子プログラムを呼び出す /
EXEC CICS LINK PROGRAM(‘MBRCHK’)
COMMAREA(CICS_COMMAREA)
LENGTH(LENGTH(CICS_COMMAREA));

/ 3. 呼び出し結果の判定 /
IF CA_RET_CD = ’00’ THEN
/ 正常終了時の処理 /
;
ELSE
/ 異常終了時の処理 /
;

RETURN;
END MBRUP;

受信側プログラム(子:MEMBERCHK)の抜粋

1
/ ======================================================== /
/ 受信側プログラム: 会員情報チェック処理 /
/ ======================================================== /
MBRCHK: PROC OPTIONS(MAIN);

/ 【超重要】DFHCOMMAREAという名前で受け取る領域を定義 /
/ 送信側の構造体と、項目名、データ型、桁数を1バイトも違わず合わせる /
DCL 1 DFHCOMMAREA,
3 CA_RET_CD CHAR(2), / 戻り値コード /
3 CA_CUST_ID CHAR(8), / 顧客ID /
3 CA_CUST_NAME CHAR(20), / 顧客名 /
3 CA_UPDATE_DAT FIXED DEC(7,0); / 更新日 /

/ ここで受信したデータを使ったバリデーションなどを実行 /
IF CA_CUST_ID = ‘ ‘ THEN DO;
CA_RET_CD = ‘E1′; / エラーコード返却 /
END;
ELSE DO;
CA_RET_CD = ’00’; / 正常コード返却 /
END;

/ CICS RETURNで制御とCOMMAREAを親に戻す /
EXEC CICS RETURN;

END MBRCHK;

—

4. アベンドを回避するための「お守り」の知見

上記のコードを見て、「あれ、子プログラムのほう、引数を受け取る書き方をしてなくない?」と気づいた方は鋭いですね。

CICSの規約として、受信側プログラムの先頭には決まって `DFHCOMMAREA` という名前の変数が(通常の外部変数と同じように)置かれることになっています。CICS基盤が勝手にメモリのアドレスをそこにバインドしてくれるため、私たちは特別なポインタ操作を意識しなくても、そのまま `CA_CUST_ID` などの変数名で中のデータにアクセスできるのです。

ここで、実務で絶対に押さえておいてほしいアベンド回避の極意(チェックポイント)を3つお伝えします。

1. インクルード(COPY句)を必ず活用する
送信側と受信側で同じ構造体を毎回手打ちでコピペするのは、バグの元です。「COPYブック(PL/Iなら `%INCLUDE` や `%MEMBER` など)」と呼ばれる共通パーツ化の仕組みを使い、両方のプログラムからまったく同じ定義ファイルを読み込ませるのが鉄則です。
2. `LENGTH` 組み込み関数を信頼する
`EXEC CICS LINK` の `LENGTH()` パラメータには、マジックナンバー(例: `LENGTH(50)` のような直値)を絶対に入れないでください。上記コードのように `LENGTH(CICS_COMMAREA)` と記述することで、コンパイラが構造体の実際のバイト数を計算し、安全に引き渡してくれます。構造体を改修したときも、長さを書き換える手間が省けます。
3. パディング(隙間)に気をつける(※上級者向け)
PL/Iのデータ属性(特に `FIXED BIN(31)` や浮動小数点数)は、マシンのメモリー境界(アライメント)に合わせるために、コンパイラの最適化によって意図しない「空きバイト(パディング)」が自動挿入されることがあります。
CICSのCOMMAREAなど、厳密なバイト単位のレイアウトが要求される通信領域では、データ属性に `ALIGNED` ではなく `UNALIGNED`(非整列)を明示的に指定して、余計な隙間が空かないようにコントロールするのが、レガシー現場のプロフェッショナルの知恵です。

—

おわりに

いかがでしたでしょうか?
「PL/Iの予約語がない緩さ」に少し驚きつつも、「構造体を使ったメモリレイアウトの同期」がいかに大切か、イメージがつかめたのではないでしょうか。

レガシーなメインフレームの仕組みも、一歩ずつ構造を分解して見ていけば、現代のWeb APIやマイクロサービスにおける「スキーマ定義(JSON SchemaやProtocol Buffersなど)」と本質はまったく同じです。

「データ構造のズレさえ防げば、メインフレームは怖くない!」
ぜひこの安心感を胸に、日々のマイグレーションや保守作業に臨んでみてくださいね。あなたのメインフレームライフを、陰ながら応援しています!

タイトルとURLをコピーしました