「数値のはずがゴミデータ」でジョブが落ちる前に:PL/I `ON CONVERSION` で防衛的プログラミングを極める
メインフレームの現場で、バッチ処理中に突然 `IBM0231S ONCODE=301` というメッセージと共にジョブが異常終了(ABEND)し、冷や汗をかいた経験はないだろうか?
いわゆる「CONVERSION条件」。外部ファイル(VSAMやQSAM)から読み込んだ数値項目に、本来入っているはずのない空白や、誤った文字データが混入している際、PL/Iが「これ、数値として扱えないよ!」と悲鳴を上げる現象だ。
今日は、システムアーキテクトとして、この「データ不一致」をスマートにさばき、ジョブを無理やり落とすのではなく、安全にログを吐いて処理を継続させるための、PL/Iの実践的な流儀を伝授しよう。
—
1. なぜ CONVERSION 条件が発生するのか
PL/Iは厳格な言語だ。例えば、文字属性のフィールドから数値属性のフィールドへ値を代入(代入文や`READ`など)する際、コンパイラは内部的に「データ変換」を行う。この時、数値項目に数値以外の文字が混入していると、PL/Iの実行時環境は即座に例外を発生させ、デフォルトではジョブを終了させる。
特に、レガシーシステムのマイグレーションにおいて、データソースが他システムからの連携ファイルである場合、この「ゴミデータ問題」は避けて通れない。
2. ONユニットによる「動的捕捉」の基本
`ON CONVERSION` ユニットを使えば、変換エラーが発生した瞬間に制御を奪い取り、エラーログを出力して処理を続行(あるいはスキップ)させることが可能だ。
実践的なサンプルコード
以下に、レコード読み込み時にデータ不一致が発生しても、ジョブを止めずにエラー箇所を特定するコーディング例を示す。
/i
/ プログラムの基本構造 /
MAIN_PROC: PROCEDURE OPTIONS(MAIN);
/ 宣言部 /
DCL IN_RECORD CHAR(80); / 入力レコード /
DCL NUM_VAL FIXED DEC(5); / 数値項目として扱いたいフィールド /
DCL FILE_IN FILE RECORD INPUT;
/ ONユニットの定義:CONVERSIONが発生した時の挙動を定義する /
ON CONVERSION
BEGIN;
/ エラー箇所の特定とログ出力 /
PUT SKIP LIST(‘!!! データ変換エラー発生 !!!’);
PUT SKIP LIST(‘発生箇所: 数値項目に不正なデータが含まれています’);
PUT SKIP LIST(‘入力データの内容: ‘ || IN_RECORD);
/
- ここで重要なのは「処理をどう継続するか」。
- 本来はエラーレコードを別ファイルへ退避させる設計にすべきだ。
/
PUT SKIP LIST(‘当該レコードをスキップし、処理を継続します。’);
END;
OPEN FILE(FILE_IN);
DO WHILE(^EOF);
READ FILE(FILE_IN) INTO(IN_RECORD);
/
- ここで代入が発生する瞬間にONユニットが発動する可能性がある。
- SUBSTRで該当箇所を切り出して代入する設計が一般的。
/
NUM_VAL = SUBSTR(IN_RECORD, 1, 5);
/ 正常処理のロジックをここに記述 /
END;
CLOSE FILE(FILE_IN);
END MAIN_PROC;
—
3. 実務で「唸る」ための3つのポイント
単に `ON CONVERSION` を書くだけでは、プロフェッショナルとは言えない。以下の点を意識してほしい。
① ONユニット内での「再試行」は要注意
`ON CONVERSION` が発生した後、デフォルトのままでは同じ場所で無限ループに陥る可能性がある。エラーが発生したフィールドには「何らかのデフォルト値(0など)」を強引に代入して制御を戻すか、フラグを立てて処理をスキップする設計が鉄則だ。
② `DATAFIELD` ビルトイン関数の活用
`ON` ユニット内では `DATAFIELD` 関数を使うことで、実際に変換に失敗した「原因となったデータ」を即座に取得できる。
/i
PUT SKIP LIST(‘エラーの原因となった文字列: ‘ || DATAFIELD);
これを使うだけで、障害調査の工数は劇的に削減される。
③ そもそも「変換」させない設計(事前チェック)
ONユニットは強力だが、多用すると実行時のオーバーヘッドになる。頻繁に不正データが混入することが分かっている場合は、`VERIFY` 関数を使って、代入前に「数値のみで構成されているか」を事前にチェックする方が、アーキテクチャとしては遥かに健全だ。
/i
/ 事前チェックの例:5桁の数値か確認 /
IF VERIFY(SUBSTR(IN_RECORD, 1, 5), ‘0123456789’) ^= 0 THEN DO;
/ 数値以外が含まれている場合の独自エラーハンドリング /
END;
—
まとめ:現場のエンジニアへ
PL/Iの `ON` ユニットは、まさに「縁の下の力持ち」だ。大規模なバッチ処理において、たった1レコードのゴミデータで数時間のジョブを無駄にするのは、システムエンジニアとして避けたい事態である。
「プログラムは、データが汚れていることを前提に書け」。これが、数々の障害現場を乗り越えてきた私の哲学だ。ぜひ、次回の改修から `ON CONVERSION` を組み込み、堅牢なバッチプログラムを作成してほしい。
何か不明な点や、特定のレガシー環境での挙動について知りたいことがあれば、遠慮なく聞いてくれ。メインフレームの深淵は、まだまだ奥が深いぞ。
