ゼロ除算の深淵:PL/Iにおける例外処理と、レガシーシステムを沈没させないための防壁
基幹系システムにおいて、0による除算(ZERODIVIDE)は単なる演算ミスではありません。それは時として、オンライン処理におけるCICSトランザクションの異常終了(ABEND)を引き起こし、DB2のコミット未了による整合性不整合を招く「時限爆弾」となり得ます。
多くのモダン言語から来た技術者が陥る罠は、例外処理を「エラーを握りつぶして無視する手段」と履き違えることです。しかし、我々が扱うPL/Iの世界では、ONユニットはシステムのリソースを保護し、制御フローを安全な退避先へ着地させるための極めて高度な「外科手術」なのです。
1. ON ZERODIVIDEによる例外トラップの基本設計
PL/IのONユニットは、スタックを遡って例外をキャッチするモダンなtry-catchとは挙動が異なります。これは「条件(Condition)が発生した際のアクションを宣言する」という、割り込みハンドラの思想に近いものです。
/i
/ ゼロ除算発生時のリカバリ処理 /
ON ZERODIVIDE BEGIN;
/ エラーログをSYSPRINTへ出力 /
PUT SKIP LIST (‘エラー: ゼロ除算が発生しました。計算ロジックを確認してください。’);
/ ゼロ除算の代わりとして、許容される最大値や安全なデフォルト値を設定 /
CALC_RESULT = 999999;
/ GOTOはONユニット内でのみ許容される特殊な制御フロー /
GOTO END_OF_CALC;
END;
/ 演算実行部 /
DIV_RESULT = TOTAL_AMT / DIVISOR;
END_OF_CALC:;
ここで重要なのは、「何のためにリカバリするのか」という目的の明確化です。バッチ処理であれば、当該レコードをエラーファイルに排出し、処理を継続させるのが定石です。しかし、CICSオンラインであれば、中途半端なデータ状態で画面を返却するより、適切にメッセージを出し、トランザクションをABENDさせてロールバックを促す方が安全な場合もあります。
2. ポインタと動的メモリ操作が招く「見えないゼロ除算」
マイグレーション時に頭を抱えるのが、`BASED`変数を用いた動的メモリ操作と計算が複合した場合です。
例えば、ポインタが指し示す構造体の中身が、通信処理の不備やメモリの破壊(オーバーレイ)によって予期せぬ値(あるいはX’00’)になっている場合、`DIVISOR`に指定した変数が意図せずゼロになることがあります。
- 教訓: ポインタアクセスを行う際は、必ずアドレッシングの正当性を検証してください。特に、パックデシマル(FIXED DECIMAL)として定義された変数が、メモリ上で不正な符号(例えばX’0C’やX’0D’以外の値)を持っている場合、演算時に例外がスローされる前にコンパイラが異常を検知することもあります。
3. コンパイラ最適化と「予期せぬ挙動」
コンパイラオプションの指定は、システムの生死を分けます。`OPTIMIZE(3)`を適用した際、ループ内の定数除算が最適化され、ループ外へ括り出されることがあります。この時、もしその定数が計算過程でゼロに変化する動的変数であった場合、最適化のロジックが先行して計算を行い、予期せぬZERODIVIDEを誘発することがあります。
大規模なマイグレーションを行う際、古いコンパイラ設定から新しい設定へ移行する際は、必ず`TEST`オプションを付与したダンプ解析を行い、最適化による命令コードの並び替えが、例外発生のタイミングにどう影響しているかを確認してください。
4. マイグレーションに向けたアーキテクチャ設計の視点
JavaやC#への移行を想定する場合、PL/IのONユニットをそのままtry-catchに置換してはいけません。以下の観点で設計を再構築する必要があります。
1. 状態の確定: PL/IのONユニットは、発生した文の次から再開することが可能です(`RETRY`)。Javaの例外処理にはないこの「文脈を保持した再試行」のロジックをどうハンドリングするか、設計段階でビジネスロジックを整理しておく必要があります。
2. パックデシマルの符号問題: COBOLやPL/Iで多用されるパックデシマルは、末尾のニブルが符号を表します。マイグレーション先でintやdecimalに変換する際、この符号の反転バグが演算ロジックを狂わせます。特にDB2の埋め込みSQLで取得した数値が、Java側でどのように解釈されるか、単体テストではなく「データ境界テスト」で徹底的に叩く必要があります。
3. ダンプ解析の文化: 汎用機でのアベンド時、システムダンプ(SYSUDUMP)は我々にとっての「事実」そのものです。移行先環境でログだけで原因を追えるか?スタックトレースが十分な情報を持っているか?今のうちから「ダンプに頼らないデバッグ体制」を構築しておくことが、移行の成功率を底上げします。
結びに代えて
PL/Iのコードは、時に難解で古めかしく見えます。しかし、その例外処理の仕組みには、数十年もの間、金融機関の基幹システムを止めることなく支え続けた「堅牢さへの執念」が刻まれています。
ゼロ除算一つとっても、単にエラーを避けるのではなく、「システムとしてどう振る舞うのが最も誠実か」という問いを立てること。それこそが、レガシーを継承するエンジニアに求められる、真のアーキテクチャ思考なのです。
次回のブログでは、`ON AREA`を用いたメモリ管理と、マイグレーションにおけるポインタ変数の「ポインタ・アリスメティック」の危険な魅力について深掘りします。
