【テクニカル・上級編】ENDFILE条件によるファイル読み込み終了の制御 – PL/Iの基本構文とデータ制御実践ガイド

終わりの見えないループに潜む罠:PL/Iの`ENDFILE`条件と「正しき」終了制御

メインフレームの現場で、夜中の3時にバッチジョブが「終わらない」というアラートで叩き起こされた経験はないだろうか。特に、レガシーシステムの移行や保守を担うテックリードにとって、PL/Iの`ON ENDFILE`ユニットは、最も馴染み深いと同時に、最も誤解されやすい「劇薬」の一つだ。

JavaやC#の`while`ループや`Iterator`に慣れ親しんだエンジニアが、PL/Iのファイル入力を読み解く際、最初に躓くのがこの「条件付き制御」の挙動である。今日は、単なる文法解説ではなく、コンパイラの裏側とマイグレーションの現場で培った「壊れないコード」の作法について語ろう。

1. `ON ENDFILE`は「例外」であるという認識

まず、大前提を叩き込んでおく必要がある。`ON ENDFILE(ファイル名)`は、ループの「終了条件」ではない。これは「例外処理」である。

以下のコードを見てほしい。これが、多くの保守現場で散見される、しかし決して推奨できない「典型的なアンチパターン」だ。

1
/ 不安定な実装例:読み込みと終了判定が混在している /
READ FILE(IN-FILE) INTO(REC-AREA);
DO WHILE(CONDITION);
/ 処理ロジック /
READ FILE(IN-FILE) INTO(REC-AREA);
END;

なぜこれが危ういのか。ファイル末尾(EOF)に達した瞬間に、プログラムの制御フローが強制的に割り込まれ、コンパイラの「ONユニット」という別世界へ飛ばされるからだ。もし、この`READ`文の直後に複雑なデータ加工やポインタ操作を配置していると、EOF検出時のスタック管理やレジスタ状態の不整合により、予期せぬABEND(S0C4やS0C7など)を誘発する可能性が高まる。

2. 推奨される実装パターン:`SIGNAL`ではなく`EOFフラグ`の併用

基幹システムの堅牢性を担保するなら、`ON ENDFILE`は最小限の「フラグ立て」に専念させるのが鉄則だ。

1
/ 堅牢な実装例:ONユニットはフラグ制御のみに徹する /
DCL EOF_FLAG BIT(1) INIT(‘0’B);

ON ENDFILE(IN_FILE) BEGIN;
EOF_FLAG = ‘1’B; / フラグを立てて静かに終了を待つ /
END;

READ FILE(IN_FILE) INTO(REC_AREA);
DO WHILE(^EOF_FLAG);
/ 1. パックデシマルの符号チェック(内部表現の反転バグに注意) /
/ 2. DB2への埋め込みSQL実行 /
/ 3. 処理後に次レコードを読み込む /
READ FILE(IN_FILE) INTO(REC_AREA);
END;

ここで重要なのは、「読み込み(READ)と判定(WHILE)のサイクルを切り離さないこと」だ。また、マイグレーション先でJava等のストリーム処理に変換する際、このフラグ制御構造はそのまま「イテレータパターン」にマッピングできるため、ロジックの変換ミスも防げる。

3. 深淵を覗く:ポインタ操作とコンパイラ最適化の副作用

さて、ここからがアーキテクトとしての腕の見せ所だ。大規模な基幹システムでは、`READ`時にバッファを直接メモリ上に展開するため、`BASED`変数とポインタ(`PTR`)を活用することが多い。

1
DCL REC_PTR PTR;
DCL REC_AREA CHAR(100) BASED(REC_PTR);

/ OSが管理するメモリ領域を直接参照する /
READ FILE(IN_FILE) SET(REC_PTR);

ここで、コンパイラオプションの`OPTIMIZE`を最大限に効かせている場合、注意が必要だ。EOF検出時にレジスタに保持されたポインタ値が、最適化によって再利用されることで、稀に「既に解放されたはずのメモリ領域」を参照しに行くという、解析泣かせのバグが発生する。

ダンプ解析の際、`S0C4`が発生したら、まずは`SYSUDUMP`で当該ポインタが指すアドレスを確認してほしい。`ENDFILE`時の`ON`ユニットが実行される前に、どこかの処理が「先走って」ポインタを書き換えていないか?あるいは、パックデシマル型のデータがEBCDIC特有の符号反転(`X’C’`が`X’F’`になる等)を起こし、桁あふれによるアベンドを誘発していないか?これらを見極めるのが、真のメインフレーム技術者の資質だ。

4. マイグレーションに向けた提言

もし君が今、このPL/IコードをJavaやC#へ移行しようとしているなら、以下の点だけは設計書に太字で書き込んでほしい。

  • エラーハンドリングの非同期性: `ON ENDFILE`という命令型言語特有の「割り込み」を、オブジェクト指向の「例外」あるいは「ステート管理」にどう置き換えるか。
  • データの型安全性: PL/Iのパックデシマル(`DECIMAL FIXED`)は、Javaの`BigDecimal`と完全に一致するわけではない。マイグレーション後の演算精度不一致は、基幹業務において致命的な「金額ズレ」を生む。
  • CICSとの親和性: もしこれがCICSオンラインプログラムなら、`ENDFILE`は`RESP`コード(`DFHRESP(ENDFILE)`)として扱う必要がある。これを考慮せずに移行すると、オンライン環境下でタスクがハングアップする。

最後に

PL/Iは古い言語かもしれないが、その設計思想には、ハードウェアの挙動を極限まで引き出すための「規律」が埋め込まれている。`ENDFILE`というたった一行のコードの中に、我々はシステムの信頼性を賭けているのだ。

次に、画面越しに「なぜか動かない」コードに出会ったとき、それは言語のせいではない。コンパイラが忠実に、そして冷酷に、君が書いた「論理の隙間」を再現しているに過ぎない。その隙間を埋めることこそ、我々アーキテクトの仕事である。

引き続き、技術の深淵を楽しんでほしい。質問があればいつでもどうぞ。

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