こんにちは。メインフレームの現場で、日々COBOLやPL/Iの巨大なソースコードと格闘している同志の皆さん。
今日は、JCLの`PARM`引数として渡された「文字データ」を、PL/Iプログラム内で`FIXED`(固定小数点数)型に安全に受け渡し、バリデーションとエラーハンドリングを行う方法について、実務のノウハウを交えて徹底的に解説しよう。
夜間バッチの真っ最中に、オペレータから「コンソールにS0C7(あるいはPL/I特有のIBM製異常終了コード)が出てジョブが落ちました!」と叩き起こされた経験はないだろうか? その原因の多くは、JCLの変更漏れや誤ったパラメータ指定、そしてプログラム側での甘い型変換とバリデーション不足にある。
今回は、この古典的かつ極めて重要なテーマについて、ベテランの視点から「二度と夜中に呼ばれないための鉄壁のコード」を伝授する。
—
1. JCL PARM引数とFIXED型の落とし穴
JCLの`EXEC`ステートメントで指定する`PARM`は、純粋な文字ストリング(CHARACTER)としてプログラムに渡される。例えば、`PARM=’12345’`と書かれていれば、それは数値ではなく「文字の ‘1’, ‘2’, ‘3’, ‘4’, ‘5’」の集まりだ。
これをPL/Iのプログラム側で計算や条件判定に使うために `FIXED BINARY` や `FIXED DECIMAL` に暗黙的、あるいは安易に明示的変換しようとすると、次のような罠にハマる。
- 半角スペースや数値以外の混入: `PARM=’1234 ‘` や `PARM=’123A5’` のようなゴミデータが混ざったとき、そのまま代入するとコンバージョンエラー(IBMメインフレームでは `IBM0221S` や `IBM0223S` などのシグナル)が発生する。
- 桁あふれ(Overflow): パラメータが想定以上の長さを持ち、受け側の`FIXED`の精度を超えた場合、上位桁が切り捨てられて予期せぬデータ破損を招く。
ここで、PL/Iの強力なメカニズムである `ON-unit`(例外処理機構) と ビルトイン関数(`VERIFY`, `CFIXED`など) を組み合わせた、実務標準の防御的プログラミングを見ていこう。
—
2. 実践PL/Iソースコード解説
以下に、JCLからのパラメータを受け取り、厳密なバリデーションを行った上で `FIXED BINARY(31)` として安全に処理するサンプルプログラムを示す。すべて大文字のIBMメインフレーム標準スタイルで記述している。
1
PARMPRC: PROC OPTIONS(MAIN);
/————————————————————–/
/ 宣言部 /
/————————————————————–/
/ JCLからのPARMを受け取るための外部記述変数 /
DCL 1 PARM_AREA,
5 PARM_LEN ALARM FIXED BIN(15), / パラメータ長 /
5 PARM_TEXT CHAR(100); / パラメータ本体 /
/ 内部処理用の固定小数点数(31ビット符号付き) /
DCL WK_LIMIT_VAL FIXED BIN(31) INIT(0);
/ ワーク用文字変数 /
DCL WK_STR CHAR(10);
DCL WK_POS FIXED BIN(15);
/ フラグ変数 /
DCL FLG_ERROR BIT(1) INIT(‘0’B);
/————————————————————–/
/ ON-unit宣言: データ変換エラーの捕捉 /
/————————————————————–/
ON CONVERSION BEGIN;
DISPLAY(‘ SEVERE ERROR: CONVERSION EXCEPTION OCCURRED ‘);
DISPLAY(‘INVALID CHARACTER IN JCL PARM. CHECK EXEC STATEMENT.’);
FLG_ERROR = ‘1’B;
GOTO PARM_ERR_EXIT;
END;
/————————————————————–/
/ メイン処理ロジック /
/————————————————————–/
/ 1. パラメータの取得(FETCH/GETではなく特殊変数PARMを使用) /
GET FILE(SYSIBN) PARM(PARM_TEXT); / ※処理系により取得方法は異なりますが /
/ 一般的にはPROCの引数として受け取ります: /
/ PARMPRC: PROC(DUMMY_PARM) OPTIONS(MAIN); のスタイルも主流です /
/ 今回は分かりやすく標準的なメインプロシージャ引数スタイルで説明 /
/ ※実務では PROC(P_PARM) OPTIONS(MAIN); DCL P_PARM CHAR(100) VARYING; が一般的 /
DISPLAY(‘RECEIVED PARM LENGTH: ‘ || TRIM(CHAR(PARM_LEN)));
DISPLAY(‘RECEIVED PARM TEXT: [‘ || PARM_TEXT || ‘]’);
/ 2. 基本長チェック(空パラメータや長すぎないかの確認) /
IF PARM_LEN = 0 | PARM_LEN > 8 THEN DO;
DISPLAY(‘ ERROR: PARM LENGTH IS INVALID. (1 TO 8 BYTES REQUIRED) ‘);
GOTO PARM_ERR_EXIT;
END;
/ 有効な長さに切り出し /
WK_STR = SUBSTR(PARM_TEXT, 1, PARM_LEN);
/ 3. バリデーション: 数字以外の文字が含まれていないか VERIFY で検証 /
/ VERIFY(string, match) は、matchに含まれない文字の位置を返す。 /
/ 0が返れば、すべてmatch(この場合は数字)で構成されている証拠。 /
WK_POS = VERIFY(WK_STR, ‘0123456789’);
IF WK_POS ^= 0 THEN DO;
DISPLAY(‘ ERROR: NON-NUMERIC CHARACTER FOUND AT POSITION: ‘
|| TRIM(CHAR(WK_POS)) || ‘ ‘);
GOTO PARM_ERR_EXIT;
END;
/ 4. 安全に FIXED BINARY へ変換 /
/ ここまでのチェックを通過しているため、CONVERSION例外は起きないはず /
WK_LIMIT_VAL = WK_STR;
DISPLAY(‘SUCCESS: CONVERTED FIXED BINARY VALUE = ‘ || TRIM(CHAR(WK_LIMIT_VAL)));
/ 通常の業務処理へ続く… /
GOTO NORMAL_EXIT;
PARM_ERR_EXIT:
/ 異常終了処理 /
DISPLAY(‘ JOB TERMINated ABNORMALLY DUE TO PARM ERROR ‘);
CALL PLIRETC(16); / リターンコード16を返して異常終了 /
STOP;
NORMAL_EXIT:
RETURN;
END PARMPRC;
—
3. アーキテクトが教えるコードの急所とテクニック
上記のコードには、何十年ものメインフレーム運用の歴史から導き出された「現場の知恵」が詰まっている。特に注目してほしいポイントを解説しよう。
① `VERIFY` ビルトイン関数の圧倒的優位性
文字データを `FIXED` 型へ代入する前に、`VERIFY(WK_STR, ‘0123456789’)` を通している点に注目してほしい。
PL/Iの `VERIFY` は、第1引数の文字列の中で、第2引数に含まれていない文字が最初に出現する位置(インデックス)を返す。すべて数字であれば `0` を返すため、これだけで「純粋な数字文字列であるか」の判定が1発で終わる。IF文で1文字ずつループを回して `IF CH < '0' | CH > ‘9’` なんてコードを書いている古臭いプログラムを見かけたら、今すぐこのビルトイン関数に書き換えさせよう。
② `ON CONVERSION` による二重の防御
どんなに事前チェックを厳重にしても、JCLの記述ミスやコンパイラの仕様変更、思わぬメモリエラーなどにより予期せぬデータが流れ込むリスクはゼロにはならない。
PL/Iには `ON CONVERSION` という強力な例外処理(ONユニット)がある。万が一、型変換時にハードウェアレベルでのデータ例外が発生しても、プログラムが即座に強制終了(S0C7など)するのを防ぎ、自前のエラーハンドリング(今回はエラーメッセージを出して `PLIRETC(16)` で安全にジョブを落とす)に制御を回収できる。
「異常な入力でプログラムを急死させず、ログを残して綺麗に終わらせる」 これが基幹系バッチの鉄則だ。
③ `FIXED BINARY(31)` の選択
メインフレーム(z/OS)のアーキテクチャ上、演算器の効率やCOBOLの `COMP`(S9(9) COMPUTATIONAL)との親和性を考慮すると、数値項目は `FIXED BINARY(31)`(フルワード)または `FIXED DECIMAL` を選択するのが最も安全かつ高速だ。
文字型から数値型へ代入する際、PL/Iコンパイラは適切な内部変換ルーチンを生成するが、その前提として「データが正しい形式であること」をプログラマが担保する責務がある。
—
おわりに
レガシーシステムのマイグレーションや保守において、JCLやパラメータ周りの仕様変更は最もデグリー(不具合)を生みやすいブラックボックスの一つだ。
「動けばいいや」と型変換をコンパイラ任せにするコードは、いつか必ず深夜のトラブルを引き起こす。
今回紹介した `VERIFY` による事前バリデーションと、`ON CONVERSION` による例外キャッチの二段構えをマスターすれば、JCLのPARM起因のトラブルは完全に根絶できる。
明日からのコーディング、そしてコードレビューの際に、ぜひこの知見を役立ててほしい。質問や「うちの現場ではこうしている」といった意見があれば、いつでも声をかけてくれ。
