ゼロ除算の悪夢をどうかわすか?PL/IのONユニットによる「大人の例外処理」
メインフレームの現場で、バッチ処理が突如として「S0CB」で落ちる……。そんな現場の緊迫した空気は、何度経験しても胃が痛くなるものだ。特に、長年動いているレガシーコードの改修時、計算ロジックに潜む「ゼロ除算」の罠は、システム全体を止める爆弾になり得る。
今日は、PL/Iにおいてこのゼロ除算(ZERODIVIDE)を、システムデフォルトのABEND(異常終了)に委ねるのではなく、エンジニアとして「管理下」に置くための作法を伝授しよう。
—
なぜシステムデフォルトではいけないのか
PL/Iのデフォルト動作では、ゼロ除算が発生するとシステムが検知し、メッセージ(IBM0514Sなど)を出力してABENDする。テスト環境ならまだしも、本番の夜間バッチでこれが発生すれば、VSAMファイルやDB2の整合性を考慮したリカバリ作業という、最もやりたくない仕事が待っている。
我々メインフレームエンジニアの矜持として、「予期せぬ例外はあっても、予期できる例外でシステムを止めない」。この原則をコードに落とし込むのが、`ON ZERODIVIDE`ユニットだ。
—
実践:ONユニットによるガードの実装
まずは、標準的なコードの書き方を見てほしい。ポイントは、ONユニットが「どこで有効になり、どこで終了するか」というスコープの管理だ。
1
TEST_CALC: PROC OPTIONS(MAIN);
/ ゼロ除算発生時のフラグ管理用変数 /
DCL ZERODIVIDE_OCCURRED BIT(1) INIT(‘0’B);
DCL RESULT FIXED DEC(15,2);
DCL (VAL_A, VAL_B) FIXED DEC(15,2);
/ ZERODIVIDE条件の捕捉開始 /
ON ZERODIVIDE BEGIN;
ZERODIVIDE_OCCURRED = ‘1’B;
PUT SKIP LIST(‘警告: ゼロ除算を検知しました。計算をスキップします。’);
/ 必要に応じて、ここでVSAMのログ出力や管理用テーブルへの書き込みを行う /
END;
/ ここにファイル読み込み処理などが続くとする /
VAL_A = 100;
VAL_B = 0;
/ 通常の計算処理 /
RESULT = VAL_A / VAL_B;
/ ONユニットの効果を終了させる場合は REVERT を使うのが通例 /
REVERT ZERODIVIDE;
IF ZERODIVIDE_OCCURRED THEN DO;
/ 異常終了させず、次のレコード処理へ進むためのリカバリコード /
END;
END TEST_CALC;
—
現場で役立つ「3つの鉄則」
現場の泥臭い改修で、このコードを組み込む際に必ず守ってほしいポイントがある。
1. REVERTを忘れるな
ONユニットは宣言したブロックを抜ければ自動的に無効化されるが、一つのプロシージャ内で処理を継続する場合、特定の計算が終わった時点で `REVERT ZERODIVIDE;` を明記しておくべきだ。意図しない場所で発生したゼロ除算まで誤って捕捉してしまい、バグの温床になるのを防ぐためである。
2. VSAM入出力との兼ね合い
VSAMの更新処理中に計算エラーが発生した場合、単にONユニットでメッセージを出して終了するだけではいけない。「どこまで更新したか」を管理するフラグや、エラーレコードを別のエラースプールに掃き出す処理と組み合わせるのが、実務的な「安全な設計」だ。
3. SIGNAL ZERODIVIDE でテストせよ
「本当に例外が発生した時に動くのか?」と不安になったら、`SIGNAL ZERODIVIDE;` 文をコードに一時的に埋め込んでテストしてほしい。これは意図的に例外を発生させる命令だ。これを実行して、意図したONユニットの処理に制御が移るかを検証する。これがユニットテストの基本中の基本だ。
—
最後に:メインフレームの守り手として
PL/Iは、その歴史の長さゆえに、こうした例外処理の仕組みが非常に強力かつ洗練されている。`ON CONDITION` は一見すると、制御フローを不透明にする「悪しきgoto」のように見えがちだが、正しく使えばこれほど心強い武器はない。
大規模バッチのマイグレーションや改修において、「止まらないバッチ」を設計するのはアーキテクトの腕の見せ所だ。後輩諸君には、ただ仕様書通りに動くコードを書くのではなく、こうした「最悪の事態」を想定した防衛的なプログラミングを身につけてほしい。
何か不明な点があれば、いつでも聞きに来るといい。メインフレームの深淵はまだまだ奥が深いぞ。
