【実務・中級編】ON CONVERSIONによるデータ変換エラーの捕捉 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは。夜間バッチの終了間際に突然発生する「S0C7」や「CONVERSION」のメッセージに、冷や汗をかいた経験は無いだろうか。

メインフレームの現場で長く生きていると、外部から連携されてくるテキストデータや、古い電文フォーマットの「野良データ」に何度も泣かされる。特に、半角スペースが混ざった数値項目や、低位バイトにゴミが入ったゾーン十進数を、うっかり数値型(FIXED DECIMALなど)へ暗黙の型変換を伴ってMOVEしようものなら、容赦なくシステムは牙を剥く。

今回は、PL/Iが誇る強力な例外処理機構の一つである `ON CONVERSION` を取り上げる。実務のバッチ改修や、オープン系へのマイグレーション調査で必ず直面する「データ変換エラーの捕捉とダンプ解析の極意」について、私の経験を交えてじっくりと解説しよう。

—

1. 予約語を持たないPL/Iと「CONVERSION条件」の正体

PL/Iの非常にユニークな特徴として、「言語としての厳密な予約語(Reserved Words)を持たない」という仕様がある。例えば、`IF` や `READ` といったキーワードであっても、プログラマが変数名として宣言して使うことが(やろうと思えば)できてしまう。コンテキストによってキーワードか識別子かをコンパイラが判断しているわけだ。

この「何でもあり」の柔軟性は時に諸刃の剣となるが、データ型に関しては非常に厳格だ。文字(CHARACTER)から数値(FIXED/FLOAT)への変換時、あるいはその逆において、表現不可能なデータ(例:数値項目にアルファベットが混ざっている、あるいは全角スペースやゴミデータがある)を処理しようとすると、PL/Iランタイムは `CONVERSION` 条件(Condition) を発生させる。

もし、この `CONVERSION` に対して何の対策も講じていない(ONユニットを書いていない)場合、プログラムはデフォルトのアクションとして異常終了(通常はABENDコード ASMA00H や関連するU3999などのユーザアベンド、あるいはシステム異常終了)を引き起こし、夜間バッチ全体を巻き込んで盛大にクラッシュすることになる。

基幹システムのエンジニアとして、外部からの不測の入力データごときでバッチ全体を止めるわけにはいかない。ここで `ON CONVERSION` の出番となる。

—

2. 実践!ON CONVERSIONによるエラーハンドリングと制御フロー

それでは、実際のコーディングパターンを見ていこう。
メインフレームの現場ではお馴染みの、VSAMファイルや順編成ファイルからのレコード読み込みを想定してほしい。数値項目にゴミが入っていた場合でも、プログラムを異常終了させずにログにエラーを出力し、該当レコードをスキップ(あるいはデフォルト値で救済)する実装だ。

以下のサンプルコードを見てほしい。大文字ベースで記述し、実務でそのまま使えるようにインデントとコメントを丁寧に入れている。

1
/ ========================================================== /
/ プログラム名: CONVDEMO /
/ 概要: VSAM入力データのCONVERSIONエラー捕捉サンプル /
/ ========================================================== /
CONVDEMO: PROC OPTIONS(MAIN);

/ — 宣言部 — /
DCL IN_FILE FILE RECORD INPUT;
DCL EOF_FLG CHAR(1) INIT(‘0’);

/ 入力レコード構造(外部から来た生データ:すべて文字として受ける) /
DCL 1 IN_REC,
5 EMP_ID_CH CHAR(5), / 社員ID(文字) /
5 EMP_SAL_CH CHAR(7); / 給与(文字で受けるが、後で数値化する) /

/ 内部演算用変数(パック十進数) /
DCL W_EMP_SAL FIXED DEC(7,2) INIT(0);

/ エラー判定フラグ /
DCL ERR_OCCURRED BIT(1) INIT(‘0’);

/ — ファイルオープン — /
OPEN FILE(IN_FILE) TITLE(‘EMPFILE’);

/ ========================================================== /
/ ON CONVERSION ユニットの定義 /
/ – 文字から数値への変換に失敗した瞬間にこのブロックに制御が移る /
/ ========================================================== /
ON CONVERSION
BEGIN;
/ エラーフラグを立てる /
ERR_OCCURRED = ‘1’B;

/ どのような不正データでコケたかをナシ(OSメッセージ)に出力 /
PUT SKIP EDIT (‘[WARN] CONVERSION ERROR DETECTED. RAW DATA: ‘,
EMP_SAL_CH) (A, A);

/

  • 対策: 変換不可能な不正データが入っていた場合、
  • ゼロに置き換えて処理を継続させる(リトライの指示)

/
W_EMP_SAL = 0;

/

  • 注意: GOTOを使わない場合、ONユニットを抜けると
  • 変換エラーを起こした代入文の「次の文」へ制御が戻る。

/
END;

/ — メイン処理ループ — /
DO WHILE (EOF_FLG = ‘0’);

READ FILE(IN_FILE) INTO(IN_REC);
IF EOF_FLG = ‘1’ THEN LEAVE;

/ 初期化 /
ERR_OCCURRED = ‘0’;

/

  • ここで文字データを数値型へ代入する。
  • もし EMP_SAL_CH に数値以外の文字(スペースやゴミ)が含まれていれば、
  • 即座に上記の ON CONVERSION ユニットが発動する。

/
W_EMP_SAL = EMP_SAL_CH;

/ CONVERSIONが発生していなければ通常処理を行う /
IF ERR_OCCURRED = ‘0’ THEN DO;
PUT SKIP EDIT (‘正常処理: ID=’, EMP_ID_CH, ‘ 給与=’, W_EMP_SAL)
(A, A, F(9,2));
;
END;
ELSE DO;
/ エラーレコードとしての別ファイルへの退避や件数カウント処理 /
PUT SKIP EDIT (‘-> 不正レコードとしてスキップします: ID=’, EMP_ID_CH) (A, A);
END;

END;

/ — 終了処理 — /
CLOSE FILE(IN_FILE);
PUT SKIP EDIT (‘— 処理正常終了 —‘) (A);

END CONVDEMO;

コードの解説と制御フローのポイント

1. `ON CONVERSION` ユニットの常駐範囲
このコードでは手続きの冒頭付近に `ON CONVERSION` を配置している。PL/Iの条件プレフィックスやONユニットは動的なスコープを持つため、この宣言以降、このブロック内で発生したすべてのCONVERSION条件を捕捉できるようになる。

2. エラー発生時の挙動と `GOTO` の罠
ONユニット内でエラーデータを補正(上の例では `W_EMP_SAL = 0`)し、そのままONユニットを抜けると、PL/Iは「エラーが解決された」とみなして、変換失敗を引き起こした代入文の次の行から処理を再開する。
ただし、もしエラーレコードを完全にバイパスして次の `READ` に飛ばしたい場合は、ONユニット内に `GOTO` 文を記述して、ループの先頭やエラー処理ルーチンへ強制的にジャンプさせる設計にすることもある。実務では設計方針(全件処理か、異常時即時アベンドか)に合わせて慎重に決める必要がある。

3. ビルトイン関数(BUILTIN)の活用
PL/Iにはデータ状態を詳細に取得するためのビルトイン関数(`ONSOURCE` や `ONCHAR` など)が用意されている。これらをONユニット内で併用することで、「具体的にどの変数の、何桁目で変換エラーが起きたのか」をSYSUDUMPやSYSOUTへ詳細に出力させることが可能だ。

—

3. 現場で役立つダンプ解析とトラブルシューティングの極意

「ONユニットを仕掛けたはずなのに、なぜか別の箇所でS0C7(データ例外)で落ちる」
レガシーシステムの保守をしていれば、誰もが一度はハマる罠だ。最後に、現場のベテランとしてのダンプ解析のコツを伝授しよう。

① `ONSOURCE` ビルトイン関数の活用

もし詳細な調査が必要な場合、ONユニット内で以下のように `ONSOURCE` を利用してほしい。

1
DCL ONSOURCE BUILTIN;
/ エラー発生時の生の文字列表現を取得できる /
PUT SKIP EDIT (‘不正なソース文字列: ‘, ONSOURCE) (A, A);

これにより、問題の変数がメモリ上でどのようなバイト列を持っていたのかが即座に判明する。例えば、パッと見はスペースに見えても、実際には `X’00’`(ヌル文字)や `X’40’` 以外のゴミが混ざっているケースをこれで一発特定できる。

② コンパイラオプションの確認

メインフレームの企業でPL/Iを扱う際、コンパイル時オプション(`TRAP`, `CHECK`, `STMT` など)の設定がデバッグの難易度を大きく左右する。
本番稼働時はパフォーマンスを考慮してチェックを外すこともあるが、テスト環境や移行期のデバッグ段階では、必ずランタイムメッセージが詳細に出力される設定(例: `CONVERSION` が有効になる設定)になっていることをJCLのPARMを確認してほしい。

③ 根本的なデータモデリングの见直し

`ON CONVERSION` はあくまで「事後的な救済措置」や「ロバスト性を高めるための防波堤」に過ぎない。
根本的な解決は、外部インタフェース仕様書の厳格化と、データ受け入れ時のバリデーションロジック(そもそも数値項目に文字が入っていないかを文字のままでチェックするサブルーチン)の事前実装にある。

—

まとめ

PL/Iの `ON CONVERSION` は、レガシーシステムの堅牢性を支える極めて強力な武器だ。
予約語を持たない自由度の高い言語仕様と相まって、適切に例外処理をデザインすることで、予期せぬデータ不良によるシステムダウンを未然に防ぐことができる。

日々の保守作業や大規模なマイグレーションプロジェクトにおいて、「なぜこのデータで止まるのか」を言語仕様の深部から理解し、的確にハンドリングできるようになれば、あなたも立派なPL/Iアーキテクトだ。
現場の信頼を勝ち取る堅牢なコードを、ぜひ次の開発でも実践してみてほしい。

タイトルとURLをコピーしました