ゼロ除算の深淵: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は、ハードウェアの制約をねじ伏せ、計算の信頼性を担保するために磨き上げられた言語です。この言語が持つ「厳格さ」を理解した者だけが、モダンな言語環境においても「落ちないシステム」を構築できるのです。
次回は、ポインタ変数による動的メモリ管理が、いかにして現代のメモリリーク問題の「教訓」となっているかについて語りたいと思います。
