現場のエンジニアへ告ぐ:PL/Iの「ON ENDFILE」を甘く見るな
やあ。今日も今日とてJCLの海に潜り、SYSLOGの山と格闘している諸君、お疲れ様。
メインフレームの世界に足を踏み入れたばかりの若手から、長年この堅牢なシステムを支えてきたベテランまで、PL/Iという言語の「自由奔放さ」には、時として頭を抱えることもあるだろう。
今回は、PL/Iの「ON ENDFILE」について話をしよう。一見すると簡単なイベント駆動に見えるが、この挙動を正しく理解していないと、大規模バッチの深夜改修で「なぜかループする」「なぜかデータが欠落する」という、ゾッとするようなバグに直面することになる。
1. PL/Iの「予約語がない」という特性と識別子
まず前提として、PL/IにはC言語のような「厳格な予約語」が存在しない。これはつまり、`IF`や`THEN`、`GOTO`といったキーワードを、変数名として使うことさえ理論上は可能だということだ(もちろん、そんなことをすればコンパイラが混乱し、可読性も地の底に落ちるが)。
この「コンテキストに依存して意味が決まる」という仕様が、実はONユニットの制御フローにおいても重要になってくる。何が言いたいかというと、「君が書いたコードの文脈によって、コンパイラが『これはキーワードか、それとも変数か』を判断している」という事実を、常に頭の片隅に置いておくべきだということだ。
2. ON ENDFILE:ファイル終了の「作法」
VSAMファイルや順次ファイルを読み込む際、`ENDFILE`条件が発生したときに何をすべきか。多くの現場で見かけるのは、フラグを立ててメインループを抜ける手法だが、これが一番安全で、かつメンテナンス性に優れている。
まずは、現場でそのまま使える実用的なコード例を見てほしい。
/i
/—————————————————————–/
/ ファイル読み込みの標準的なコーディング例 /
/—————————————————————–/
TEST_PROC: PROC OPTIONS(MAIN);
DCL INPUT_FILE FILE RECORD INPUT ENV(VSAM);
DCL EOF_FLAG BIT(1) INIT(‘0’B);
DCL RECORD_AREA CHAR(80);
/ 1. ON ENDFILEユニットの定義 /
/ ここでEOF_FLAGを立てて、メインループへ通知する /
ON ENDFILE(INPUT_FILE) BEGIN;
EOF_FLAG = ‘1’B;
END;
OPEN FILE(INPUT_FILE);
/ 2. メインループ:可読性を重視するならDO WHILEが基本 /
READ FILE(INPUT_FILE) INTO(RECORD_AREA);
DO WHILE(EOF_FLAG = ‘0’B);
/ データの処理ロジック /
CALL PROCESS_DATA(RECORD_AREA);
READ FILE(INPUT_FILE) INTO(RECORD_AREA);
END;
CLOSE FILE(INPUT_FILE);
/ 正常終了の明示 /
PUT SKIP LIST(‘BATCH JOB FINISHED SUCCESSFULLY.’);
END TEST_PROC;
3. 注意:ONユニット内でのGOTOは「劇薬」である
ここからが本題だ。ONユニット内から、`GOTO`文を使ってループの外へ脱出するというテクニックがある。これを使えば、フラグ変数を管理する必要がなくなり、コードが短く見える。
しかし、大規模バッチの保守において、ONユニットからのGOTOは推奨しない。
なぜか?それは、ONユニットはあくまで「割り込み処理」だからだ。スタックの深さや、コンパイラが内部的に保持している制御ブロックの復旧において、予期せぬ挙動を引き起こす可能性がある。特に、複雑なネストを持つ古いプログラムでこれをやると、デバッグ時にスタックトレースが追えなくなり、深夜の運用担当者を地獄へ突き落とすことになる。
もし、どうしてもループを即座に抜けたい場合は、必ず「ONユニット内ではフラグを立てるだけに留め、メインループ側で判定する」こと。これが、20年、30年と稼働し続けるシステムを守るための唯一の「作法」だ。
4. ベテランからのアドバイス
PL/Iのコードを書く際、`BUILTIN`関数(例えば`SUBSTR`や`INDEX`)を活用するのは当然として、以下の3点を意識してほしい。
1. インデントの徹底: `BEGIN;` ~ `END;` のブロック構造は、誰が見ても視覚的に一意に決まるようにインデントせよ。
2. 大文字記述の維持: メインフレーム環境では、ソースは大文字で統一するのが基本だ。小文字混じりのコードは、コンパイラの設定次第で思わぬ警告を生むことがある。
3. 例外処理の意識: `ON ENDFILE`だけでなく、`ON ERROR`や`ON CONVERSION`も定義しておくことで、予期せぬデータ異常(文字コードの不正や数値変換エラー)が発生した際、ダンプリストを吐かせて沈黙するのではなく、ログを出しつつ美しく終了させることができる。
PL/Iは古い言語と言われるが、その堅牢さと柔軟性は、現代のどの高級言語にも引けを取らない。君たちが書くその一行が、明日も日本の基幹システムを動かし続けるのだ。
また何か壁にぶつかったら、いつでもここへ来い。健闘を祈る。
