こんにちは!メインフレームの荒波へようこそ。
JavaやCOBOLといったモダン、あるいは従来型のビジネス言語をバリバリ書いてきた方にとって、IBMメインフレームの世界、そして「PL/I」というちょっとレトロで奥深い言語は、最初は少し近寄りがたく見えるかもしれませんよね。
「なんだか変数宣言のルールが独特だし、ポインタや条件制御の概念も難しそう……」
そんな風に身構えてしまうお気持ち、とてもよく分かります。でも、大丈夫ですよ。一つひとつの仕様を紐解いていけば、PL/Iは実は非常に合理的で、プログラマの意図に忠実に動いてくれる頼もしい相棒です。
今回は、そんなPL/Iの数ある挙動の中でも、現場のエンジニアを最も冷や汗させ、夜間バッチの異常終了(ABEND)を引き起こす常習犯——「CONVERSION(コンバージョン)条件」と、そのデータ例外の正体にスポットを当てて、優しく、そして深く解説していきますね。
—
1. 他言語出身者が驚く、PL/Iの「予約語を持たない」世界観
まず、PL/Iの世界へ飛び込むにあたって、頭を柔らかくしておかなければならない最大の特徴があります。それは、「PL/Iには、厳密な意味での『予約語(Keyword)』が存在しない」ということです。
JavaやCOBOLであれば、`IF`、`ELSE`、`DISPLAY`、`MOVE`といった単語は予約されており、変数名として使うことはできませんよね。しかし、PL/Iのコンパイラは、その前後の「文脈(Context)」を読んで、「お、ここは変数名だな」「おっ、ここは処理命令だな」と空気を読んで判断してくれます。
例えば、極端な話ですが、以下のようなコードもコンパイルエラーにはなりません。
DECLARE IF FIXED BIN(31); / 「IF」という名前の数値型変数 /
IF = 100; / 変数IFに100を代入 /
コンパイラは「最初の `IF` は宣言だから変数だな」「次の `IF =` は……文脈的に代入だな」と賢く解釈します。
「なんて柔軟なんだ!」と感動する一方で、この柔軟さが災いして、うっかり予約語っぽい名前を変数に使ってしまうと、コードを読む人間が混乱する原因にもなります。ですから、実務ではあまり奇抜な真似はせず、お行儀よく分かりやすい名前をつけるのがエンジニアの美学とされています。
—
2. 数値型データストレージの落とし穴:CONVERSION条件とは?
さて、本題の「CONVERSION条件」についてです。
JavaやCOBOLから来た人が一番ハマりやすいのが、このデータ型の扱いです。特に、メインフレーム特有の「ゾーン十進数(DISPLAY形式)」や「パック十進数(COMP-3形式)」といった、バイナリや文字の境界線上にあるデータストレージ属性を扱うとき、この問題が牙を剥きます。
CONVERSIONが発生するメカニズム
例えば、プログラム内で「ここは純粋な数字が入る場所だよ」と定義した数値型の変数に対して、「スペース」や「英字」「ゴミデータ」が混ざった領域からデータを移動(代入)させようとした瞬間、PL/Iのランタイム環境はこう叫びます。
> 「おいおい!数字として読めない文字が入ってきたぞ!計算できないよ!」
これが、CONVERSION(変換エラー)条件の発生です。
COBOLであれば、NUMERIC CHECK機能などでスルーしたり、あるいは単にスペースをゼロとして扱ってくれたりする寛容さがあったりしますが、PL/Iのデフォルトの挙動はストイックです。「データが汚れている」と検知した瞬間、容赦なくプログラムを異常終了(S0CDなどのシステムABEND、またはPL/I独自のメッセージIBM360Iなど)に追い込みます。
ざっくりイメージ:自動変換の裏にある罠
PL/Iは、異なるデータ型の間で代入を行う際、親切心から「勝手に型変換(暗黙の型変換)」を行おうとします。
文字型(CHARACTER)の変数に入っている `”1234A”` という文字列を、数値型(FIXED DECIMAL)の変数に入れようとしたとき、コンパイラは「文字列を数値に直してあげなきゃ!」と頑張ります。しかし、末尾の `A` が邪魔をして変換しきれず、ここでプツンと糸が切れてしまう——これがCONVERSIONの正体です。
—
3. 実践!CONVERSION例外を優しく手なづけるコード例
では、実際のPL/Iコードを見てみましょう。
もし対策を何もしない場合、不正なデータが入ってきただけでバッチが即死してしまいます。それを防ぐために、PL/Iには「ONユニット(条件監視機能)」という強力な仕組みが用意されています。
以下は、ファイルから読み込んだ文字列データを数値型に変換する際に、もしCONVERSIONが発生してもプログラムを落とさず、安全にトラップして救済するサンプルコードです。
CONV_TEST: PROC OPTIONS(MAIN);
/ 変数の宣言 /
DCL RAW_DATA CHAR(5) INIT(’12A45′); / 意図的に数字の中に英字’A’を入れた汚れたデータ /
DCL NUM_VAL DEC FIXED(5,0); / 純粋な数値型変数 /
DCL ERROR_FLG BIT(1) INIT(‘0’B); / エラー検知フラグ /
/ ========================================================== /
/ ON条件の定義:CONVERSIONが発生した時の救命措置をここに書く /
/ ========================================================== /
ON CONVERSION BEGIN;
ERROR_FLG = ‘1’B; / エラーフラグをONにする /
PUT SKIP LIST(‘【警告】数値変換できない不正なデータを検知しました: ‘ || RAW_DATA);
/ 発生源のデータを安全な値(例えばゼロ)に書き換えて続行させる /
NUM_VAL = 0;
/ 注意:このGOTOがないと、エラー発生元の次の行へ正しく復帰できない場合があります /
GOTO CONV_RECOVER;
END;
PUT SKIP LIST(‘— 処理開始 —‘);
/ ここで文字型から数値型への暗黙の変換が発生し、CONVERSION条件が発火する /
NUM_VAL = RAW_DATA;
CONV_RECOVER:
/ ONユニットからの復帰ポイント /
IF ERROR_FLG THEN
PUT SKIP LIST(‘処理は継続されましたが、不正データのため値は 0 で代替されました。’);
ELSE
PUT SKIP LIST(‘正常に変換された値: ‘, NUM_VAL);
END;
PUT SKIP LIST(‘— 処理終了 —‘);
END CONV_TEST;
コードの解説とポイント
1. `ON CONVERSION BEGIN … END;`
これがPL/Iの真骨頂です。Javaでいう `try-catch` ブロックのようなもので、「もしこのプログラムのどこかで数値変換エラーが起き私を呼んでくれ!」とランタイムに監視を命じています。
2. 安全な代替処理
エラーが発生した際、ただ慌ててプログラムを落とすのではなく、ログ(`PUT LIST`)を出力し、変数に `0` を代入して事なきを得ています。
3. レガシー特有の救済フロー
`GOTO CONV_RECOVER;` を使っている点に注目してください。「古い言語だなあ」と感じるかもしれませんが、例外発生箇所から安全に処理の合流地点へ戻るために、PL/Iの現場ではこのONユニットとGOTOの組み合わせが長年愛されてきた定番テクニックなのです。
—
4. 不正データの特定と、根本的な解決に向けて
バッチ処理中にCONVERSIONが発生したとき、一番大変なのは「一体、どのファイルの、何件目のレコードの、どの項目にゴミが入っていたのか」を突き止めることです。
実務の現場における調査の鉄則をいくつかお伝えしておきますね。
- ダンプの活用(SYSUDUMP / CEEDUMP)
PL/Iが強制終了した際に出力されるダンプには、エラーが発生した瞬間のプログラムのオフセット(アドレス)が残っています。ロードモジュールとコンパイルリスト(Listing)を突き合わせることで、「ソースコードの何行目でCONVERSIONが起きたか」を秒速で特定できます。
- 事前のバリデーション(文字チェック)をケチらない
ONユニットでトラップするのも大事ですが、そもそもデータベースやマスターへ書き込む前、あるいは外部ファイルを読み込んだ瞬間に、フィールドが本当に数字だけで構成されているかをチェックする自前ルーチン(あるいはPL/Iの組み込み関数 `VERIFY` など)を通すのが、最も王道で確実な防御策です。
—
おわりに
いかがでしたでしょうか?
PL/Iの予約語がない独特の構文ルールや、データ型に厳格であるがゆえの「CONVERSION条件」も、仕組みさえ分かってしまえばもう怖いものではありませんよね。
メインフレームのシステムは、何十年も動き続けてきた歴史の重みがあります。その分、データも一筋縄ではいかない「癖」を持っていますが、PL/Iの持つ強力な例外処理(ON条件)を味方につければ、どんな汚れたデータがやってきても華麗にいなし、システムを守り抜くことができます。
レガシーの世界へ足を踏み入れたばかりのあなたの不安が、この記事で少しでも解消され、日々の開発や調査のヒントになればエンジニアとしてこれ以上ない喜びです。
それでは、快適なメインフレームライフを!
