おい、最近の若手はオープン系の言語ばかり触っているから、メインフレームの保守に入った途端に「夜間バッチが突然ABENDした! 原因が分からない!」と青い顔をして走ってくる。ログを見せてもらうと、決まって出てくるのがS0C7(データ例外)か、あるいはPL/I特有のCONVERSION条件による異常終了だ。
聞いてくれ。COBOLだってデータ例外には悩まされるが、PL/Iの`CONVERSION`条件は、言語仕様の懐の深さゆえに、正しくハンドリングしないとデバッグで泥沼にハマる。特にVSAMファイルや外部から流れ込んでくる「汚れたデータ」を扱うとき、PL/Iがどう振る舞うのかを理解していないと、現場で痛い目を見る。
今日は、この`CONVERSION`条件の発生メカニズムから、現場で使えるスマートな捕捉・特定手法まで、骨太に解説しよう。
—
1. なぜCONVERSIONは起きるのか? PL/Iの「優しさ」と「罠」
まず大前提として、PL/IにはCOBOLのような「予約語の縛りが緩い」という特徴と同時に、データ型変換に関して「暗黙の型変換(Implicit Conversion)」を非常に積極的に行うという仕様がある。
例えば、文字型(CHAR)の領域に入っているデータを、算術型(FIXED DECIMALなど)の変数に代入しようとしたとき、コンパイラは「おっ、よしなに数値に変換してやるか」と気を利かせて裏で処理を走らせる。
しかし、その中身がスペースだったり、英字が含まれていたり、あるいはゾーン10進数(DISPLAY形式)の符号部分が壊れていたりしたらどうなるか?
「おいおい、これどうやって数字に直すんだよ!」とコンパイラ(というか実行時ライブラリ)が音を上げる。これがCONVERSION条件の発生原因だ。
予約語を持たない言語仕様の弊害?
よく「PL/Iには予約語がない(すべて文脈依存の識別子だ)」と言われるが、これはプログラマブルな制御を行う上で強力な反面、変数名に `ON` や `CONVERSION` といったキーワードに近い名前をうっかり付けてしまい、コンパイルエラーや予期せぬ混乱を招く原因にもなる。データ定義の甘さと相まって、この「融通の利く仕様」が現場のバッチを夜な夜な止める主犯格なのだ。
—
2. ONユニットによるCONVERSIONの捕捉制御
PL/Iの真骨頂は、こうしたハードウェア/ソフトウェア的な例外を、構造化されたONユニット(ON-unit)で優雅に(あるいは泥臭く確実に)トラップできる点にある。
もし `ON CONVERSION` を仕込んでおかないと、システムは容赦なくエラーメッセージ(IBMなら `IGZ` や `IBM`系のランタイムメッセージ)を吐いて異常終了(ABEND)する。だが、ONユニットを適切に配置すれば、異常終了を回避して「不正データスキップ処理」や「エラーログの出力」を行わせることが可能になる。
ここで重要なのが、制御フローの把握だ。
CONVERSIONが発生した瞬間、処理は即座にONユニットにジャンプする。そして、問題の不正データをどう処理するか(修正するのか、無視するのか)を決定し、`GOTO` で抜けるか、あるいはそのまま処理を続行させるかを制御する。
—
3. 実践!VSAMファイル読み込みとCONVERSION捕捉プログラム
百聞は一見に如かず。実際のメインフレーム開発現場を想定した、実用的なPL/Iのバッチプログラムを見てみよう。
KSDS(Key-Sequenced Data Set)のVSAMファイルからレコードを読み込み、金額フィールドへ数値化する際に、万が一の不正データ混入に備えるコードだ。
1
CONVTEST: PROC OPTIONS(MAIN);
/————————————————————–/
/ ワーキングストレージ定義 /
/————————————————————–/
DCL WS-EOF-FLG CHAR(1) INIT(‘0’);
DCL WS-REC-COUNT FIXED BIN(31) INIT(0);
DCL WS-ERR-COUNT FIXED BIN(31) INIT(0);
/ VSAM入力ファイル(生データは文字型として安全に受ける) /
DCL IN-FILE FILE RECORD INPUT
ENV(VSAM);
/ レコードレイアウト(外部からのデータはすべてCHARで受けるのが鉄則)/
DCL 1 IN-RECORD,
5 IN-EMP-ID CHAR(5),
5 IN-EMP-NAME CHAR(20),
5 IN-SALARY-X CHAR(8); / 文字列としての給与データ /
/ 算術演算用変数 /
DCL WK-SALARY FIXED DEC(9,2);
/————————————————————–/
/ ファイルオープン /
/————————————————————–/
OPEN FILE(IN-FILE);
/————————————————————–/
/ CONVERSION条件の捕捉(ONユニットの定義) /
/————————————————————–/
ON CONVERSION
BEGIN;
WS-ERR-COUNT = WS-ERR-COUNT + 1;
DISPLAY(‘ 警告: CONVERSION条件発生 (データ異常) ‘);
DISPLAY(‘ 対象社員ID : ‘ || IN-EMP-ID);
DISPLAY(‘ 不正文字列 : ‘ || IN-SALARY-X);
/ ここで安全なデフォルト値を強制代入して処理継続を図る /
WK-SALARY = 0;
/ 注意: GOTOでONユニットを抜けるのが安全な場合が多い /
GOTO CONV-BYPASS;
END;
/————————————————————–/
/ メインループ /
/————————————————————–/
DO WHILE(WS-EOF-FLG = ‘0’);
READ FILE(IN-FILE) INTO(IN-RECORD);
IF ENDFILE(IN-FILE) THEN
WS-EOF-FLG = ‘1’;
ELSE
DO;
WS-REC-COUNT = WS-REC-COUNT + 1;
/ ここで暗黙の型変換が発生し、数値化できない場合にONユニットへ飛ぶ /
WK-SALARY = IN-SALARY-X;
CONV-BYPASS:
/ 正常処理・あるいはバイパス後の処理 /
/ 実際の業務ロジック(集計など)をここに記述 /
;
END;
END;
/————————————————————–/
/ 終了処理 /
/————————————————————–/
CLOSE FILE(IN-FILE);
DISPLAY(‘————————————————–‘);
DISPLAY(‘ 読み込み総件数 : ‘ || TRIM(WS-REC-COUNT));
DISPLAY(‘ 変換エラー件数 : ‘ || TRIM(WS-ERR-COUNT));
DISPLAY(‘————————————————–‘);
RETURN;
END CONVTEST;
—
4. 現場のシニアが教える「不正データの特定手法」とデバッグの極意
上記のコードを見て、「おっ、ONユニットでトラップしてゼロに置き換えれば安全じゃないか」と思ったそこの君。実務はそんなに甘くない。
データを勝手にゼロに置き換えてバッチを正常終了させたら、翌朝、経理部から「給料が全員ゼロになっているふざけたデータがあるぞ!」と怒りの電話がかかってくるのがオチだ。エラーを隠蔽するのではなく、「どこに、どんなゴミデータが入っているか」を正確に特定し、上流工程や前日バッチの不具合をあぶり出すのがメインフレームエンジニアの腕の見せ所だ。
実務で使えるテクニック
1. 入力は必ず `CHAR`(文字型)で受ける
VSAMやQSAMからの入力レコード(`IN-RECORD`)定義の中で、数値項目であっても最初から `FIXED DEC` や `FIXED BIN` で受けては絶対ダメだ。ファイル側がすでに汚れている場合、`READ` した瞬間にプログラムがクラッシュし、ONユニットすら発動する前に落ちることがある。必ず `CHAR` で受けてから、ワークエリアへの代入時に型変換を走らせることで、例外をコントロール下に置く。
2. `ONSOURCE` ビルトイン関数の活用
PL/Iには、CONVERSIONを引き起こした「まさにその問題の文字列」を指し示す `ONSOURCE()` という強力なビルトイン関数がある。上のサンプルではフィールドを直接参照しているが、より汎用的なエラーハンドリング共通ルーチンを作る場合は、`ONSOURCE()` を使うことで、どの変数が原因で悲鳴を上げたのかを動的にログ出力できる。
3. ダンプ(SYSUDUMP / 16進数CWDUMP)の併用
どうしても原因不明のバイナリ混入(例えばパック十進数 `COMP-3` の符号ニブルの破壊など)がある場合は、ONユニット内で詳細なスナップショットを出力するか、あえて一度ABENDさせてCEEDUMP解析に持ち込む勇気も必要だ。
—
おわりに
PL/Iの `CONVERSION` 条件との付き合い方は、いわば「信頼しすぎないこと」に尽きる。コンパイラの親切心(暗黙の変換)に全幅の信頼を置くのではなく、外部からの入力はすべて「牙を隠した野生の獣」だと思え。
しっかりとしたONユニットの設計と、文字型での安全な受取、そして的確なログ出力。これができるようになれば、君も一人前のメインフレーム・アーキテクトだ。夜間バッチの荒波に怯えず、優雅にコードをコントロールしてくれ。健闘を祈る!
