夜中の3時、オペレータからの冷ややかな電話で叩き起こされる――メインフレームの保守メンバなら、誰もが一度は経験する悪夢だ。
「ジョブABC1234が異常終了しました。コンコードはU4038、メッセージはCEE3204Sです」
画面の向こうで青ざめている後輩の顔が目に浮かぶようだ。
「先輩、またあの『固定小数点オーバーフロー』です。昨日追加した金額計算のロジックだと思うんですが、原因がさっぱり……」
おいおい、落ち着け。こういう時こそ、PL/Iの真骨頂である例外処理メカニズムが火を吹く。今日は、言語仕様の奥底にある「予約語を持たない自由な識別子」の背景と、レガシー基幹システムの生命線である `ON FIXEDOVERFLOW` を使った堅牢なエラーハンドリングについて、実務の現場目線で徹底的に叩き込んでやろう。
—
1. なぜPL/Iには「予約語」がないのか?
まず、PL/Iの根本的な思想についておさらいしておこう。
C言語やJavaを見慣れた現代のプログラマからすると驚きかもしれないが、PL/Iには厳密な意味での「予約語(Reserved Words)」が存在しない。
`IF` や `DO`、さらには今回のテーマである `FIXEDOVERFLOW` さえも、コンテキスト(文脈)によって変数名やプロシージャ名として自由に使用できる。
1
/ こんな狂った(しかし文法上は正しい)コードも書けてしまう /
DECLARE FIXEDOVERFLOW FIXED DEC(5, 0);
FIXEDOVERFLOW = 12345;
なぜこんな仕様になっているのか?
それは、IBMが1960半ばにこの言語を設計した際、「プログラマが自分の使いたい名前を自由に使えるべきだ」という強烈な思想があったからだ。コンパイラは、前後の文脈を完璧に解釈して「これはキーワードか、それともプログラマが定義した変数か」を判断する。
しかし、この「自由の代償」は大きい。もしうっかりシステム共通のビルトイン関数名や条件名を自分勝手な変数名に再定義(オーバーライド)してしまったり、コーディング規約を逸脱した命名を行ったりすると、コンパイルエラーすら起きずに、実行時になってから予期せぬ不可解な挙動を引き起こす。
基幹システムの改修現場において、「動いているから触るな」の神話の裏には、こうしたコンテキスト依存の魔物が潜んでいることを忘れてはならない。
—
2. 恐怖のランタイムメッセージ「CEE3204S」の正体
さて、本題の `CEE3204S` だ。
これはLanguage Environment(LE)ランタイム環境からのメッセージであり、「Fixed-point overflow(固定小数点オーバーフロー)」が発生し、それが適切に捕捉されずにジョブの異常終了(U4038など)に至ったことを示している。
金額計算のフィールド定義が `PIC S9(7)V99`(合計9桁、整数7桁、小数2桁)の領域に対して、桁あふれを起こすような巨大なデータが流入したり、乗算によって桁数が膨れ上がったりした瞬間にこの割り込みが発生する。
メインフレームの世界では、データの欠損や切り捨て(Truncation)を黙ってスルーすることは許されない。データが壊れているなら、即座に検知して安全に異常終了させるか、あるいは適切な例外処理(ONユニット)でトラップして代替処理に回す必要がある。
—
3. `ON FIXEDOVERFLOW` による例外捕捉の設計
PL/Iには、ハードウェアレベルの割り込みを高級言語の構文でエレガントに捕捉する `ON条件`(ON-unit)という強力なメカニズムが備わっている。
これを使えば、オーバーフローが発生した瞬間にプログラムがクラッシュするのを防ぎ、エラーログを出力した上で、安全にデフォルト値を代入したり、トランザクションをスキップしたりする制御が可能になる。
実践:VSAMファイルを読み込み、計算処理でオーバーフローを制御するプログラム
実際のバッチ処理を想定したサンプルコードを示す。大文字ベースで記述し、実務でそのまま使えるインデントとコメントを入れている。
1
/ ========================================================== /
/ PROGRAM-ID: OVFLTEST /
/ DESCRIP : 固定小数点オーバーフロー制御とON条件の実装例 /
/ ========================================================== /
OVFLTEST: PROC OPTIONS(MAIN);
/ — 変数宣言 — /
DCL WS-EOF-SW CHAR(1) INIT(‘0’);
DCL WS-RC FIXED BIN(31) INIT(0);
/ VSAM入力レコード(仮の定義) /
DCL 1 IN-RECORD,
5 IN-ACC-NO CHAR(8),
5 IN-BASE-AMT FIXED DEC(9,2),
5 IN-RATE FIXED DEC(3,3);
/ 演算結果格納エリア(意図的に桁数を小さくしてオーバーフローを誘発しやすくする) /
DCL WS-CALC-RESULT FIXED DEC(7,2);
DCL WS-ERROR-COUNT FIXED BIN(31) INIT(0);
/ VSAMファイルの定義 /
DCL ACCTFILE FILE RECORD SEQUENTIAL INPUT
ENVIRONMENT(BUFFERS(4));
/ — ファイルオープン — /
OPEN FILE(ACCTFILE);
/ ====================================================== /
/ ON条件の定義:固定小数点オーバーフローの捕捉 /
/ ====================================================== /
ON FIXEDOVERFLOW
BEGIN;
/ エラーカウンターのインクリメント /
WS-ERROR-COUNT = WS-ERROR-COUNT + 1;
DISPLAY(‘【警告】固定小数点オーバーフローを検出ししました。’);
DISPLAY(‘ 対象口座番号: ‘ || IN-ACC-NO);
DISPLAY(‘ 基準金額 : ‘ || IN-BASE-AMT);
/ オーバーフロー発生時の安全なフォールバック値(ゼロ)を設定 /
WS-CALC-RESULT = 0;
/ 割り込み元の次の命令へ制御を戻す(これがレジューム処理) /
GOTO OVFL-BYPASS;
END;
/ — メイン処理ループ — /
DO WHILE (WS-EOF-SW = ‘0’);
READ FILE(ACCTFILE) INTO(IN-RECORD);
IF ENDFILE(ACCTFILE) THEN
WS-EOF-SW = ‘1’;
ELSE
DO;
/ 通常の計算ロジック(ここでオーバーフローの可能性がある) /
/ IN-BASE-AMT が非常に大きく、かつ演算時に桁あふれする場合 /
WS-CALC-RESULT = IN-BASE-AMT (1 + IN-RATE);
/ 通常処理の合流地点 /
OVFL-BYPASS:
/ 処理結果の出力または後続処理への引き渡し /
CALL WRITE-PROCESS-LOG(IN-ACC-NO, WS-CALC-RESULT);
END;
END;
/ — ファイルクローズ — /
CLOSE FILE(ACCTFILE);
DISPLAY(‘バッチ処理正常終了。エラー検出件数 = ‘ || WS-ERROR-COUNT);
RETURN;
/ ログ出力用内部プロシージャ /
WRITE-PROCESS-LOG: PROC(P-ACC, P-RES);
DCL P-ACC CHAR(8);
DCL P-RES FIXED DEC(7,2);
/ ここに実際の出力処理やDB更新を記述 /
END WRITE-PROCESS-LOG;
END OVFLTEST;
—
4. 現場のシニアアーキテクトから後輩へのアドバイス
このコードを見て、「なるほど、`ON` ユニットを使えば異常終了せずに処理が続けられるんだな」と安易に納得してはいけない。ここからがシニアの腕の見せ所だ。
1. `GOTO` による脱出の危険性を知れ
上記のコードでは、`ON` ユニット内から `GOTO OVFL-BYPASS;` でループ内に復帰している。PL/Iの `ON` ユニット内からのジャンプは仕様上許されているが、スパゲッティコードの温床になりやすい。本当にそのレコードを救うべきなのか、それともトランザクション単位でロールバックすべきなのか、業務要件と照らし合わせて設計しなければならない。
2. コンパイラオプション(STGIO / TEST / CHECK など)の確認
マイグレーション案件などで、古いCOBOL資産をPL/Iに置き換えたり、古いPL/Iコードを最新の Enterprise PL/I コンパイラでリコンパイルする際、デフォルトの演算モード(DEC15/DEC31など)やオーバーフローの判定基準が変わることがある。コンパイル時のオプションで `FIXEDOVERFLOW` が有効(`SIZE` や `OVERFLOW`)になっているかを必ず確認しろ。
3. デバッグの基本は「データクレンジング」
プログラム側でオーバーフローを `ON` ユニットでむやみに握りつぶすのは、実は悪手であることもある。根本原因は「上流工程から流れてくるマスターデータの精度不良」であることが9割なのだから。例外処理でジョブを止めずに動かすことだけに酔わず、必ず `WS-ERROR-COUNT` などを監視し、JESログに明確な証跡を残すコーディング標準をチーム全体で徹底してほしい。
—
基幹システムのコードは、何十年も前に先輩たちが血汗を流して作り上げた巨大な城のようなものだ。その一部をいじるには、言語の仕様を表面的な理解で済ませてはならない。
バグが出たとき、「なぜそのエラー(CEE3204S)が起きたのか」、コンパイラの挙動とハードウェアの割り込みの仕組みまで頭の中でイメージできるようになれば、お前も一人前のメインフレームエンジニアだ。
さあ、コーヒーでも飲んで、次のジョブストリームの確認に取り掛かろうか。
