おい、最近入った若手が「PL/Iって変数名に `IF` とか使っても怒られないんですね、すげえや」なんて呑気に笑っているのを聞いて、私は思わず冷や汗をかいたよ。
おいおい、ちょっと待て。それが通るからって、何でもかんでも自由にしていいわけじゃないんだ。PL/Iには予約語という概念がほぼ存在せず、文脈で解釈される柔軟性がある。だがな、その自由度の高さゆえに、一歩間違えるとコンパイルエラーにもならず、本番稼働後にデータが音もなく腐っていくという、最悪の悪夢を見ることになる。
今回は、その代表格であり、夜間バッチの現場で何度もエンジニアたちの肝を冷やしてきた「ON FIXEDOVERFLOW条件」について、骨の髄まで叩き込んでやろう。
—
なぜPL/Iの「桁あふれ」は恐ろしいのか?
メインフレームで動く勘定系や基幹バッチの多くは、金額や数量の計算に `FIXED DECIMAL` や `FIXED BINARY` を使っている。ここで問題になるのが、計算結果が変数の定義桁数を超えてしまったときの挙動だ。
C言語やJavaなんかだと例外(オーバーフロー)が飛んで即座に異常終了するか、あるいは強烈な切り捨てが起きる。しかし、PL/Iのデフォルトの仕様では、何も言わずに上位桁をスパッと切り捨てて、何食わぬ顔で処理を続行する。
これが何を意味するか分かるか?
例えば、100億円を扱うはずのフィールドが、桁あふれによって下位の数桁だけに切り詰められ、突然「数万円」としてDBやVSAMファイルに書き込まれてしまう。エラーログすら残らない。朝に出社して、帳票の合計値と実際の残高が全く合わないことに気づいたときの、あの胃がキリキリするような絶望感を、お前らにも味あわせたくはないんだ。
デフォルトで無効化されているという「爆弾」
PL/Iの歴史的背景から、演算性能を最優先した結果、`FIXEDOVERFLOW` などの条件はデフォルトでDISABLED(無効)に設定されている。つまり、プログラマが明示的に「桁あふれを監視しろ」と指示しない限り、コンパイラはオーバーフローのチェックコードを生成しない。
これを防ぐための強力な武器が、ONユニット(ON-unit)による例外トラップだ。
実務の現場では、VSAMファイルからの大量レコード読み込みや、複雑な月利計算を行うバッチプログラムにおいて、この `ON FIXEDOVERFLOW` を適切に配置することが、システムの信頼性を担保する最後の砦となる。
—
実践:ON FIXEDOVERFLOW を組み込んだ堅牢なバッチプログラム
百聞は一見に如かずだ。実際の現場で使える、実戦形式のPL/Iソースコードを用意した。大文字ベースの記述、適切なインデント、そして `BUILTIN` 関数の活用法をしっかり目に焼き付けてくれ。
このサンプルは、VSAM(KSDS)から売上データを読み込み、単価と数量を掛け合わせた結果を別のマスターに反映させるという、よくある日次バッチの一部を模したものだ。
1
/ —————————————————- /
/ プログラム名: SLSMSTUP – 売上実績算定バッチ /
/ 概要: FIXEDOVERFLOW条件を監視し、桁あふれを検知する /
/ —————————————————- /
SLSMSTUP: PROC OPTIONS(MAIN);
/ 宣言部 /
DCL VSAM_IN FILE RECORD INPUT;
DCL EOF_FLG CHAR(1) INIT(‘0’);
/ 売上レコード構造体 /
DCL 1 SLS_REC,
5 EMP_ID CHAR(6), / 社員ID /
5 ITEM_CNT FIXED BIN(15), / 数量 /
5 UNIT_PRC FIXED DEC(7,2), / 単価 /
5 TTL_AMT FIXED DEC(9,2); / 金額(計算先)/
/ 異常終了コード格納用 /
DCL RET_CODE FIXED BIN(31) INIT(0);
/ — ① ON FIXEDOVERFLOW 条件の有効化とトラップ — /
ON FIXEDOVERFLOW
BEGIN;
DISPLAY(‘【SEVERE ERROR】FIXEDOVERFLOW(演算桁あふれ)が発生しました。’);
DISPLAY(‘該当社員ID: ‘ || EMP_ID);
DISPLAY(‘処理を中断し、異常終了コードを設定します。’);
RET_CODE = 12;
/ 必要に応じてダンプ採取を指示 /
SIGNAL ERROR;
END;
/ 演算時のオーバーフロー監視を明示的に有効化 /
OPEN FILE(VSAM_IN5) TITLE(‘SALESDAT’);
ON ENDFILE(VSAM_IN) EOF_FLG = ‘1’;
/ メイン処理ループ /
READ FILE(VSAM_IN) INTO(SLS_REC);
DO WHILE (EOF_FLG = ‘0’);
/ 演算監視を有効化して計算を実行 /
BEGIN;
/ 念のため局所的に条件を有効化 /
(FIXEDOVERFLOW):
SLS_REC.TTL_AMT = SLS_REC.ITEM_CNT SLS_REC.UNIT_PRC;
END;
/ 正常なレコードの書き込み処理(省略) /
/ CALL WRITE_MASTER(SLS_REC); /
READ FILE(VSAM_IN) INTO(SLS_REC);
END;
CLOSE FILE(VSAM_IN);
RETURN;
END SLSMSTUP;
コードの解説とベテランの急所突込
1. `ON FIXEDOVERFLOW BEGIN … END;` の構造
ここで桁あふれ発生時の挙動を定義している。単にプログラムを落とすだけでなく、どのレコード(今回の例では `EMP_ID`)で溢れたのかを `DISPLAY` でログに吐き出させることが、デバッグ作業を劇的に楽にするコツだ。現場で「どのデータが原因で落ちたか分からない」という状況ほど絶望的なものはないからな。
2. `(FIXEDOVERFLOW):` プレフィックスの活用
ブロック単位や文単位で個別に条件の有効・無効を制御できるのがPL/Iの渋いところだ。すべての処理で監視すると微小なオーバーヘッドになるが、重要なお金の計算を行うブロックではこのように明示的にプレフィックスを付与する。これがプロのアーキテクトの仕事というものだ。
3. ビルトイン関数と例外の連動
`SIGNAL ERROR;` を発行することで、PL/Iの標準的なエラーハンドリングフローに載せ、シスログへのメッセージ出力やスマートなABEND(異常終了)へと持ち込むことができる。
—
移行・保守現場での心構え
オープン系からメインフレームにやってきたエンジニアや、浅い経験しかいないプログラマは、「コンパイルが通ったから大丈夫」という幻想を抱きがちだ。だが、PL/Iにおいては、「コンパイルエラーが出ない=安全」ではない。
既存の古いプログラムを改修する際は、必ずそのブロックで `FIXEDOVERFLOW` がどのように扱われているか、コンパイラオプションやソース内のプレフィックスを確認しろ。もし無効化されたまま大規模なデータ型の拡張(例: `FIXED DEC(7,2)` から `FIXED DEC(11,2)` への変更など)を行えば、思わぬところで上位桁が削られるサイレントバグの温床になる。
いいか、機械は正直だが、書かれたコードの通りにしか動かない。桁あふれという目に見えない爆弾からシステム手帳と顧客の信頼を守り抜くのは、画面の前に座っているお前自身の技量にかかっているんだ。
次の改修案件からは、この `ON FIXEDOVERFLOW` を必ず設計に組み込むこと。いい返事を期待しているぞ。
