【実務・中級編】ON ENDFILEユニットの制御フロー – PL/Iの基本構文とデータ制御実践ガイド

おい、最近の若手はC#やJavaの感覚でPL/Iを触るもんだから、ファイル終了(ENDFILE)の制御で盛大にやらかすことが多い。今日も「なんかバッチが無限ループしてCPUを食い潰してるんですけど!」って青い顔して駆け込んできた後輩がいたよ。

原因は決まって、`ON ENDFILE` ユニットの挙動を舐めていることだ。
他の言語みたいに `while (read() != null)` なんてスマートにはいかないのがメインフレームのリアル、そしてPL/Iの奥深さよ。今日は、この `ON ENDFILE` と、そこからの生還に不可欠な `REVERT` について、現場の血と汗が染み込んだ知見を叩き込んでやる。心して聞け。

—

1. PL/Iの「予約語レス」な世界とONユニットの正体

まず、PL/Iの根本的な思想を理解しておかないと話が始まらない。
C言語やJavaと違って、PL/Iには「厳格な予約語(Reserved Words)」がほとんど存在しない。例えば、`READ` や `OPEN`、さらには今回の主役である `END` や `ENDFILE` さえも、実はコンテキストキーワードに過ぎない。つまり、君が変数名に `ENDFILE` と名付けても、コンパイラは文脈から判断してエラーにしない(やらない方が身のためだがね)。

この「自由度の高さ」が、時として恐ろしい罠になる。

非同期割り込みとしてのONユニット

他のモダン言語の例外処理(`try-catch`)は、コードの「流れの中」で例外をキャッチするよね。しかし、PL/Iの `ON` 条件(Condition)は、ハードウェアやOSの割り込みに近い非同期のシグナル処理なのだ。

ファイル入出力(QSAMの順編成ファイルやVSAM)でファイルの終端(EOF)に達した瞬間、CPUは実行中の命令をパッと中断し、システム側で定義された `ON ENDFILE` ユニットへ制御を強制的にジャンプさせる。この「横やりが入る」ような制御フローの癖を掴んでいないと、デバッグ時に痛い目を見る。

—

2. 無限ループ地獄と `REVERT` の必須性

`ON ENDFILE` を語る上で絶対に外せないのが `REVERT` 構文 だ。ここを理解していないエンジニアが多すぎる。

ファイル読み込みのループ内で、もし `ENDFILE` 条件が発生したらどうなるか?
1. ファイルの終端に達する。
2. `ON ENDFILE` ユニットが発動し、内部の処理(フラグをONにするなど)が実行される。
3. ここが重要: ONユニットの処理が終わると、制御は「例外が発生したREAD文の直後」に戻る。
4. 次のループで再度 `READ` を実行すると、再びENDFILE条件が発火し、同じONユニットに飛び込む。

これが、先ほど後輩がやらかした「無限ループ」の正体だ。
一度検知したファイル終了イベントは、もうそのファイルに対しては用済み。だからこそ、ONユニット内で処理を終えたら、あるいは別のスコープに抜ける際には、`REVERT ENDFILE(ファイル名);` を明示的に実行して、条件の有効範囲を元に戻してやらなければならない。

—

3. 実践!堅牢なバッチ処理のPL/Iコード例

百聞は一見にしかずだ。実際の基幹系バッチで通用する、極めて標準的かつ堅牢なファイル読み込みのコードパターンを見せてやろう。大文字でビシッと書かれたこの美しい構文を脳裏に焼き付けろ。

1
/ ========================================================== /
/ プログラム名: 顧客マスタ順次読み込みサンプル /
/ 概要 : QSAMファイルの読み込みとENDFILE制御の模範例 /
/ ========================================================== /
CUST_RPT: PROC OPTIONS(MAIN);

/ — ファイル定義 (DD名: CUSTIN) — /
DCL CUSTIN FILE RECORD INPUT;

/ — 顧客レコード構造体 — /
DCL 1 CUST_REC,
5 CUST_ID CHAR(5),
5 CUST_NAME CHAR(30),
5 CUST_STATUS CHAR(1);

/ — 制御フラグ — /
DCL EOF_FLAG BIT(1) INIT(‘0’B);
DCL READ_COUNT FIXED BIN(31) INIT(0);

/ — ファイルオープン — /
OPEN FILE(CUSTIN) INPUT;

/ — ON ENDFILE ユニットの定義 — /
/ ファイル終端に達した瞬間に割り込みが入り、このブロックが実行される /
ON ENDFILE(CUSTIN) BEGIN;
EOF_FLAG = ‘1’B; / 終了フラグをONにする /

/ 【重要】必ずREVERTを発行し、無限ループや不要な二重捕捉を防ぐ /
REVERT ENDFILE(CUSTIN);
END;

/ — メイン処理ループ — /
DO WHILE(^EOF_FLAG);

/ レコード読み込み /
READ FILE(CUSTIN) INTO(CUST_REC);

/
万が一、読込直後にEOF_FLAGが立っていなかった場合の保険として、
明示的に条件判定を入れるか、あるいはONユニットに処理を委ねる。
今回はONユニット経由でEOF_FLAG=’1’Bとなる。
/
IF ^EOF_FLAG THEN DO;
READ_COUNT = READ_COUNT + 1;

/ 実際の業務処理(ここではダンプ出力の代わりにカウントのみ) /
PUT SKIP EDIT (‘READ ID: ‘, CUST_ID, ‘ NAME: ‘, CUST_NAME)
(A, A, A, A);
END;

END;

/ — ファイルクローズ — /
CLOSE FILE(CUSTIN);

PUT SKIP EDIT (‘正常終了: 総読込件数 = ‘, READ_COUNT) (A, F(10));

END CUST_RPT;

コードの解説とシニアからのワンポイントアドバイス

1. `ON ENDFILE(CUSTIN) BEGIN; … END;` のスコープ
この `ON` ユニットは、このプロシージャの有効期間中ずっと生きている。ファイルが複数ある場合は、ファイルごとに `ON ENDFILE` を定義する必要があるから、ごちゃ混ぜにならないように注意しろ。
2. `REVERT` の位置
ONユニットの内部の最後に `REVERT ENDFILE(CUSTIN);` を置いている。これにより、OSやランタイムに対する「このファイルのEOF監視は一旦リセットね」というシグナルになり、安全にループを抜け出すことができる。
3. VSAM (KSDS/ESDS) の場合との違い
VSAMの等価な終端検知は `ENDFILE` ではなく、`READ` 文の `KEY` や `INVALID KEY`、あるいは `ENDFILE` 条件の組み合わせになる。VSAMを扱うときは、ステータスコード(`STATUS`ビルトイン関数など)を併用したエラーハンドリングが主流になるが、基本の「割り込みと復帰」の思想は同じだ。

—

4. デバッグの現場で生きる知見

もし、運用中のバッチが「突然動かなくなった」「終端で固まる」と言われたら、まずは以下のポイントを確認しなさい。

  • `REVERT` 忘れはないか?

今日の後輩がまさにこれだった。ONユニット内でフラグは立てたものの、`REVERT` を書いていなかったため、永遠に同じレコードの読み込み位置周辺でループし続けていた。

  • ONユニットのスコープ(有効範囲)の勘違い

PLシグナルの有効範囲は、動的な呼び出しスタックに依存する。サブルーチンを挟んだ先で `ON` ユニットを定義し直しているつもりが、予期せぬ親のブロックのONユニットに引っかかっているケースがある。

  • ビルトイン関数の活用

コンパイラの最適化や厳密な型チェックを通すためにも、IO状態を確認する際は `ONCODE()` などのビルトイン関数を組み合わせ、ログにコードを出力する癖をつけろ。何が起きたのかが一発で分かる。

メインフレームの寿命は長い。私たちが書く(あるいは保守する)PL/Iコードは、何十年も企業の基幹を支え続けるんだ。ただ動けばいいやではなく、こうした言語仕様の裏側まで理解した上で、美しく堅牢なコードを後世に残してくれよ。期待しているぞ。

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