こんにちは。大規模バッチの改修や、オープン系へのマイグレーション案件で幾度となく夜を徹してきたシニア・アーキテクトの私だ。
君たちが今向き合っているメインフレームの基幹システム、その心臓部で静かに、しかし強烈にデータを支えているのがPL/Iという言語だ。COBOLのように「何となく読める」といった甘えを許さず、C言語並みのハードウェアに近いプリミティブな制御を可能にする、実に奥が深い言語だよ。
さて、今回はPL/Iのデータ制御における隠れた名脇役、いやトラブルメーカーと言った方が正しいかもしれない「VARYING属性の制御ブロック構造」について徹底的に解説しよう。
現場で「なぜか項目の途中にゴミデータが混ざる」「VSAMへの書き込みでパディングがおかしい」「サブスクリーンの転記でアブノーマルエンド(ABEND)する」といった怪奇現象に遭遇したことはないか? その原因の多くは、VARYING変数がメモリ上でどうレイアウトされているかを知らないことからきている。
今日の講義で、そのブラックボックスを完全にハッキリさせていこう。
—
1. VARYING文字列の正体:先頭2バイトの「長さ情報」
C言語の文字列は末尾のヌル文字(`0x00`)で終端を判断するが、PL/Iの世界は違う。特に `CHARACTER (n) VARYING` や `BIT (n) VARYING` として宣言された可変長データは、実データの直前に、そのデータの長さを格納する「2バイトのプレフィックス(接頭辞)」を持っている。
メインフレームのアーキテクチャ(大端:Big Endian)において、この先頭2バイトは「2進整数(HALFWORD / FIXED BINARY(15,0))」として解釈される。
+——————-+—————————————–+
| 長さ情報 (2バイト) | 可変長データ本体 (1 ~ n バイト) |
+——————-+—————————————–+
例えば、最大長50バイトのVARYING文字列に `”IBM”` という3文字を格納した場合のメモリ上のイメージはこうだ:
- 先頭2バイトのヘッダ: `00 03` (16進数で3を表す)
- データ本体: `C9 C2 D4` (EBCDICコードの ‘I’, ‘B’, ‘M’)
この仕様を知らないと何が起きるか?
例えば、VSAMファイルや順次ファイル(QSAM)へレコードをそのまま(`WRITE`文などで)出力する際、VARYING変数をそのまま突っ込むと、先頭の2バイト(長さ)が意図せずレコードの先頭に書き出されてしまうのだ。
ファイルレイアウト定義書には「50バイトの項目」と書いてあるのに、ダンプを取ったら最初の2バイトが文字化けしている……この手のトラブルは、レガシー保守の現場では「あるある」の筆頭格だよ。
—
2. 動的メモリ管理の内部挙動とストレージ
VARYING属性のもう一つの重要な側面が、動的なストレージ割り当てだ。
固定長(`NONVARYING`)であれば、コンパイル時にレコードやワーキング・ストレージのオフセットが完全に確定し、静的にメモリが割り当てられる。しかし、`VARYING` が絡むと話が変わる。
1. 最大長に基づくメモリ確保:
`DECLARE C_VAR CHARACTER(100) VARYING;` と宣言した場合、コンパイラは「最大100バイト+長さ制御用の2バイト = 102バイト」の領域をその変数のために静的(または自動)に確保する。
2. 値代入時の長さ更新:
変数に短い文字列を代入すると、データ本体の領域はそのまま残るが、先頭2バイトの長さカウンタのみが書き換わる。つまり、物理的なメモリサイズ自体がシュリンクするわけではなく、「有効な長さ」が動的に変化する仕組みだ。
3. 内蔵関数(BUILTIN)との連携:
PL/Iには、この長さや実態を安全に扱うための強力なビルトイン関数が用意されている。後述するサンプルコードでも活用するが、`LENGTH` や `MAXLENGTH` を正しく使い分けることがバグ防衛の要となる。
—
3. 実践コード:VARYING変数の制御とファイル入出力の罠
百聞は一見に如かず。実際にVARYING変数を定義し、その長さ制御や、VSAM/レコード入出力時の注意点を網羅したPL/Iのサンプルプログラムを見てみよう。
このコードは、VARYING文字列の動的な挙動を確認しつつ、ファイル出力時に「長さプレフィックス」をどのようにハンドリングすべきかを示す実戦的なものだ。
————————————————————–
- プログラム名: VARYTEST
- 概要: VARYING属性のデータ制御と内部構造のハンドリング
————————————————————–
VARYTEST: PROC OPTIONS(MAIN);
— 宣言部 —
- 最大長50バイトのVARYING文字列と、比較用の固定長文字列
DECLARE V_NAME CHARACTER(50) VARYING;
DECLARE F_NAME CHARACTER(50) NONVARYING;
- 作業用変数
DECLARE W_LEN FIXED BIN(15);
DECLARE W_MAX FIXED BIN(15);
DECLARE I FIXED BIN(31);
- 出力用レコード(ファイル定義を模した構造体)
DECLARE 1 OUT_REC,
5 REC_LEN FIXED BIN(15), データ長(2バイト)
5 REC_DATA CHARACTER(50); 実データ本体
- ファイル定義(例: 順次ファイル)
DECLARE OUTFILE FILE RECORD OUTPUT
ENVIRONMENT(FB RECSIZE(52));
ON ERROR
BEGIN;
DISPLAY(‘ ERROR OCCURRED IN VARYTEST ‘);
STOP;
END;
OPEN FILE(OUTFILE) OUTPUT;
———————————————————-
- 1. VARYING変数の代入とビルトイン関数の挙動
———————————————————-
V_NAME = ‘IBM MAINFRAME SYSTEMS’;
- LENGTHは現在の有効長(この場合は20)を返す
W_LEN = LENGTH(V_NAME);
- MAXLENGTHは宣言された最大長(50)を返す
W_MAX = MAXLENGTH(V_NAME);
DISPLAY(‘CURRENT LENGTH : ‘ || W_LEN);
DISPLAY(‘MAXIMUM LENGTH : ‘ || W_MAX);
———————————————————-
- 2. レコード出力時の制御(VARYINGの罠を回避する)
———————————————————-
- 【重要】
- VARYING変数をそのままWRITEすると先頭2バイトが二重になるか、
- 想定外のバイナリが混入するため、明示的に長さを制御して転記する。
———————————————————-
- V_NAMEの現在の実データを、固定長領域へ安全にスライスしてコピー
REC_LEN = LENGTH(V_NAME);
REC_DATA = ”; 初期化
- 組み込み関数SUBSTRを使い、有効文字数分だけを確実に転記する
SUBSTR(REC_DATA, 1, REC_LEN) = V_NAME;
- レコードを出力
WRITE FILE(OUTFILE) FROM(OUT_REC);
———————————————————-
- 3. 文字列が変更された場合の挙動確認
———————————————————-
V_NAME = ‘Z/OS COBOL TO PL/I MIGRATION PROJECT’;
REC_LEN = LENGTH(V_NAME);
REC_DATA = ”;
SUBSTR(REC_DATA, 1, REC_LEN) = V_NAME;
WRITE FILE(OUTFILE) FROM(OUT_REC);
CLOSE FILE(OUTFILE);
DISPLAY(‘ VARYTEST NORMALLY ENDED ‘);
RETURN;
END VARYTEST;
—
4. シニアからの現場アドバイス:デバッグと改修の心得
このサンプルコードの「2」のセクション、ここが今日の最大のポイントだ。
REC_LEN = LENGTH(V_NAME);
SUBSTR(REC_DATA, 1, REC_LEN) = V_NAME;
レガシーシステムのマイグレーションや改修において、「古いCOBOLの固定長レイアウト」と「新しく導入されたPL/IのVARYING構造」が混在するインターフェース設計に出くわすことがよくある。
何も考えずに `WRITE FILE(…) FROM(V_VARYING_VARIABLE);` などとやると、コンパイラは親切心(あるいは仕様通り)に先頭の2バイト長をそのままファイルに書き込んでしまう。その結果、後続のプログラム(COBOL製など)がデータを読み込んだ瞬間にフィールドズレを起こし、大惨事になる。
トラブルシューティングの勘所
1. ダンプ解析時は先頭2バイトを疑え:
ABEND時のストレージダンプやファイルスナップで、文字列の頭に `00 0F` のような16進数が挟まっているように見えたら、それはVARYINGのプレフィックスだ。データが左に2バイトズレていると確信して間違いない。
2. インターフェースでは極力 `NONVARYING` を使え:
プログラム内部のワーキングストレージで文字数が激しく変わるロジック(文字列結合を繰り返すなど)には `VARYING` が非常に有効だ。しかし、他システムとの連携ファイルやDB2のホスト変数、通信電文(エリア)に絡む領域では、バグの温床になるため、あえて `NONVARYING`(固定長)で設計し、空白パディングを自前でコントロールする方が、長い目で見れば保守性が高い場合が多い。
基幹システムのアーキテクチャは、一朝一夕には構築できない。しかし、こうした言語仕様の「床下」の構造をしっかりと理解していれば、どんな不可解な不具合であっても、必ずロジカルに原因を突き止め、スマートに解決できるようになる。
後輩の皆さん、レガシーの底力をナメてはいけない。だが、恐れることもない。仕組みさえ分かれば、PL/Iほど信頼性の高い言語はないのだからね。さあ、次のタスクに取り掛かろうか。
