境界を越えるな――PL/IにおけるSUBSCRIPTRANGEの賢明な扱い方
現場の若い衆から「なぜか夜間バッチで突発的にABENDするが、原因が特定できない」という泣き言を聞くたびに、私は決まってこう問い返す。「お前、配列の添字チェックは入れているか?」と。
PL/Iは、その柔軟性と強力なデータ操作能力ゆえに、一歩間違えればメモリ破壊という深淵を覗くことになる。特に配列の境界違反は、サイレントにデータを破壊し、数時間後に全く関係のない箇所で異常終了を引き起こす「悪魔のバグ」の筆頭だ。
今回は、この悪魔を鎮め、かつ開発効率を最大化するための`ON SUBSCRIPTRANGE`の使いこなしについて、現場の知見を叩き込んでおこう。
—
1. なぜ「OFF」のまま放置してはいけないのか
PL/Iのコンパイラオプションにおいて、`SUBSCRIPTRANGE`を有効にすると、プログラムは全ての配列アクセスに対して「お前、今どこのメモリを見てるんだ?」と監視を始める。
当然、このチェックは実行時のオーバーヘッドを生む。何百万件ものレコードを処理する基幹バッチで、全てのアクセスを監視すれば、CPU時間は増大する。だからこそ、多くの現場では本番環境でこのオプションを外す。だが、「本番では外すから開発でもチェックしなくていい」という考えは、プロのエンジニアとしては失格だ。
バグは開発環境で潰す。そのための最も強力な武器が`ON SUBSCRIPTRANGE`ユニットである。
—
2. 実践:ONユニットによるガードの実装
以下に、実務で使えるテンプレートを示す。重要なのは、単にエラーを出すだけでなく、「どの添字で」「どの配列が」「どう溢れたか」をログに吐き出すことだ。
/i
DEMO_PROC: PROC OPTIONS(MAIN);
/ 境界チェックの有効化はコンパイルオプションで行うが、 /
/ ONユニットはプログラム内で制御する /
ON SUBSCRIPTRANGE BEGIN;
PUT SKIP LIST(‘ 境界違反が発生しました ‘);
PUT SKIP LIST(‘配列名: ‘, ONFILE(), ‘ (疑似的な情報源)’);
PUT SKIP LIST(‘違反した添字: ‘, ONCHAR());
/ 必要に応じてダンプを出力し、即座にABENDさせる /
CALL PLIDUMP(‘T’, ‘ABEND’);
END;
DCL ARRAY(10) FIXED BIN(15);
DCL I FIXED BIN(15);
/ わざと範囲外へアクセスしてみる /
DO I = 1 TO 11;
ARRAY(I) = I 10;
PUT SKIP LIST(‘ARRAY(‘, I, ‘) = ‘, ARRAY(I));
END;
END DEMO_PROC;
—
3. VSAM入出力と配列の「境界」
実務でよくあるのが、VSAMのレコード長と、プログラム内で定義した配列のサイズの不一致だ。特に、COBOLからPL/Iへ移行したソースで見かける「OCCURS句をそのまま配列に翻訳した」ようなケースでは、レコードのレイアウト変更時に、配列の定義上限を修正し忘れる事故が多発する。
ここで`ON SUBSCRIPTRANGE`があれば、`READ`した瞬間に異常を検知できる。デバッグ時に `SYSUDUMP` を解析する時間を考えれば、実行時のログ出力にどれほどの価値があるかは言うまでもないだろう。
—
4. ベテランからのアドバイス:運用環境での使い分け
私が後輩に伝えている「鉄の掟」は以下の3点だ。
1. 開発・テスト環境は必ず `SUBSCRIPTRANGE` オプションを付与してコンパイルせよ。
- 計算機資源をケチってバグを本番に持ち込む方が、よほどコストがかかる。
2. 本番環境では、要件に応じて判断せよ。
- 極めてシビアな性能が求められる超大規模バッチならOFFにする判断もアリだが、その場合はコードレビューで「配列の境界判定」が論理的に完璧であることを証明しなければならない。
3. `BUILTIN` 関数の `HBOUND` を活用せよ。
- ハードコーディングされた添字ほど信用できないものはない。ループを回す際は必ず `DO I = LBOUND(ARRAY, 1) TO HBOUND(ARRAY, 1);` と書く癖をつけること。
—
最後に:プロの矜持
PL/Iの強力な機能は、それを扱う人間の規律があって初めて輝く。エラーを隠蔽するのではなく、エラーを可視化し、制御下に置く。それこそが、何十年も動き続ける基幹システムを支えるアーキテクトの姿勢だ。
次回の改修では、お前のコードにこの `ON` ユニットが美しく組み込まれていることを期待しているぞ。何かあれば、いつでも相談に来い。
