【テクニカル・上級編】ON ZERODIVIDEによるゼロ除算の例外ハンドリング – PL/Iの基本構文とデータ制御実践ガイド

ゼロ除算の深淵:PL/Iにおける堅牢な例外処理と、レガシー移行への「遺言」

メインフレームの現場で何十年と稼働してきたPL/Iプログラム。そのコードの隅々に宿る「堅牢さ」の正体は、コンパイラの気まぐれではなく、プログラマが仕掛けた周到な罠(トラップ)にあります。特に、計算処理の要所で行われるゼロ除算(ZERODIVIDE)のハンドリングは、基幹システムの安定稼働を左右する境界線です。

今回は、単なる構文解説ではなく、後のマイグレーションで地雷とならないための、PL/I流・例外ハンドリングの極意を紐解いていきましょう。

1. ゼロ除算を「握りつぶす」ことの真意

PL/Iの`ON ZERODIVIDE`は、単にアベンド(S0CB)を回避する道具ではありません。Javaの`try-catch`と大きく異なるのは、システム全体の状態を維持したまま、計算の「継続」か「放棄」を選択できる点です。

まずは、実務でよく見られる堅牢なハンドリング例を示します。

/i
/ — メイン処理構造 — /
MAIN_PROC: PROCEDURE OPTIONS(MAIN);

/ ゼロ除算ハンドラの定義:これを設定しないとシステムはS0CBへ直行する /
ON ZERODIVIDE BEGIN;
/ システムログへ詳細を出力。ここでDB2のSQLCAを参照することもある /
PUT SKIP LIST(‘ERROR: ゼロ除算が発生しました。計算処理をスキップします。’);

/ 異常終了を避けるためにフラグを立ててメインフローへ戻す /
GOTO CALC_ERROR_EXIT;
END;

/ パックデシマル演算の例 /
DCL DIVIDEND FIXED DECIMAL(15, 2) INIT(100.00);
DCL DIVISOR FIXED DECIMAL(15, 2) INIT(0.00);
DCL RESULT FIXED DECIMAL(15, 2);

/ 危険な演算 /
RESULT = DIVIDEND / DIVISOR;

CALC_ERROR_EXIT:
/ ここからリカバリ処理を開始する /

END MAIN_PROC;

なぜこれが重要か

基幹バッチにおいて、1件の除算エラーで数時間かかるジョブを落とすことは許されません。しかし、`ON-UNIT`を多用しすぎるとコードのフローがスパゲッティ化し、後任の保守担当者を絶望させます。重要なのは、「どこでエラーが起き、どこでハンドラが拾うか」というスコープを最小限に制御することです。

2. コンパイラ最適化と「予期せぬ挙動」

PL/Iのコンパイラオプション(`OPTIMIZE(3)`など)を有効にすると、生成されるマシン語は驚くほど効率的になります。しかし、ここで問題になるのが「コードの順序入れ替え」です。

極端に最適化されたコードでは、`ON-UNIT`が捕捉するべき例外が発生する前に、変数の値がレジスタ上で先行して処理され、デバッグダンプと実際のソースコードの乖離を招くことがあります。特に、CICSオンライン処理で`EXEC CICS`コマンドとPL/Iの演算が混在する場合、コンパイラは時として、我々が意図しないタイミングで例外をトリガーします。

  • Tips: 重要な計算ロジックが含まれるセクションでは、あえて `NOOPTIMIZE` を指定する勇気を持ってください。数ミリ秒の性能を削って、数日間の保守工数を買う。これがアーキテクトの判断です。

3. マイグレーションに向けた「埋め込みSQL」と「符号反転」の罠

現在、PL/IからJava/C#への移行を進めるプロジェクトが増えています。その際、最も苦しむのが「データ型の解釈」です。

  • パックデシマル(COMP-3)の内部構造:

PL/Iの`FIXED DECIMAL`は、物理的にはパックデシマルで保存されます。特に符号部分(X’0C’やX’0D’など)が、移行先のJavaの`BigDecimal`でどう扱われるか。もしCOBOLやPL/Iで「符号反転バグ(意図的に符号を壊してフラグにする手法)」が使われていた場合、移行先で例外が発生するのは必至です。

  • 埋め込みSQL(DB2):

PL/Iの`ON ZERODIVIDE`は、DB2のSQL実行結果(SQLCODE)には関与しません。SQLでゼロ除算が発生した場合は、`WHENEVER SQLERROR`で捕捉する必要があります。両方のレイヤーで例外ハンドリングが混在するコードは、移行時に論理が断絶しやすく、最もバグを埋め込みやすいポイントです。

4. アーキテクトとしてのアドバイス

もし、あなたが今PL/Iのコードを読んでいて、`ON ZERODIVIDE`がプログラムの冒頭(PACKAGEまたはPROCEDUREの最初)でグローバルに宣言されていたら、それは「技術的負債」の警告信号です。

1. スコープを絞る: `BEGIN; … END;` ブロックを使い、例外の影響範囲を局所化してください。
2. ダンプを恐れるな: ABENDが発生した場合、CEEDUMPを読み解くスキルは現代でも通用する強力な武器です。どのモジュールの、どのオフセットでゼロ除算が起きたのか。それを突き止めることが、次世代システムへの最大のドキュメントになります。
3. 移行を見据える: PL/Iが持つ「強力だが危険な機能」を、Javaの例外処理にそのまま直訳しようとしないでください。その処理が「なぜ必要なのか」という業務要件レベルまで引き戻して再設計することが、真のマイグレーションです。

PL/Iは、ハードウェアの制約をねじ伏せ、計算の信頼性を担保するために磨き上げられた言語です。この言語が持つ「厳格さ」を理解した者だけが、モダンな言語環境においても「落ちないシステム」を構築できるのです。

次回は、ポインタ変数による動的メモリ管理が、いかにして現代のメモリリーク問題の「教訓」となっているかについて語りたいと思います。

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