こんにちは。長年、金融や流通の基幹システムでIBMメインフレームとPL/Iに向き合ってきたシステムアーキテクトの私だ。
今日のテーマは、PL/Iにおける「ENVIRONMENT属性のSCALARVARYING指定と可変長文字列のメモリレイアウト」についてだ。
メインフレームの現場で、C言語やCOBOL、あるいはオープン系のJavaやPythonなどとのデータ連携、あるいはVSAMファイルやQSAM(順編成ファイル)の入出力で「文字化けが起きる」「レコード長が想定とズレる」「オフセットが2バイトズレてパニックになった」といったトラブルに直面したことはないだろうか?
その原因、大抵はPL/I特有の可変長文字列(VARYING属性)の内部表現と、`SCALARVARYING`オプションの有無を正しく理解していないことに起因している。今日はこの厄介な罠と、実務で絶対に押さえておくべきコーディングの極意を授けよう。
—
1. PL/Iの可変長文字列(VARYING)の基本と暗黙の罠
まず、PL/Iにおける可変長文字列の基本をおさらいしておこう。
PL/Iでは、以下のように `VARYING`(または `VAR`)属性を付与して変数を宣言する。
DCL
WK_NAME CHAR(50) VARYING;
COBOLに慣れたエンジニアだと、可変長といえば「長さを示すフィールド(通常はCOMPの2バイト)+データ本体」という構造体を思い浮かべるだろう。PL/Iの `VARYING` 文字列も概念的にはこれに近いが、言語仕様上、データ領域の先頭に「2バイトの長さ接頭辞(Length Prefix)」が自動的に付付加される仕組みになっている。
ここで重要なのは、この可変長文字列が「スカラー変数」として扱われる場合と、「構造体(Structure)のメンバーや配列」として扱われる場合で、ファイル出力時の挙動が大きく変わるという点だ。
—
2. SCALARVARYING属性とは何か?なぜ必要なのか?
デフォルトの状態では、PL/Iの構造体に含まれる可変長文字列(VARYING)をレコード入出力(WRITE / READ)で外部ファイル(VSAMやQSAM)に書き出す際、コンパイラは「構造体全体の可変長情報」として最適化を試みる。
しかし、他言語(例えばC言語で書かれたデーモンや、COBOLのREDEFINESで受け取るプログラム)との間でファイルを直接共有する場合、このPL/I独自の構造体パディングや長さ接頭辞の扱いが災いし、データが盛大にズレる。
ここで登場するのが、ファイル定義(DECLAREのFILE属性、あるいはENVIRONMENT(SCALARVARYING))だ。
- SCALARVARYING未指定(デフォルト):
構造体内のVARYINGフィールドは、PL/I内部の制御ブロック構造に依存した形で扱われる。
- SCALARVARYING指定:
ファイルに対して、「このデータセットに含まれる可変長文字列は、個々のスカラーとしてのVARYING規則(先頭2バイトの長さ+実データ)に厳密従って書き出し・読み込みを行う」ことをコンパイラおよびランタイムに明示する。
特に、VSAMのKSDSやRRDSに対して、可変長レコード(RECFM=V または VB)でデータを出し入れする際、この指定を誤ると、読込時に `DOM (Data Exception)` や `STRINGRANGE` などの厄介なハード異常を引き起こす原因となる。
—
3. 実践:SCALARVARYINGを活用したVSAM入出力プログラム
百聞は一見に如かず。実際に `SCALARVARYING` を意識した、堅牢なPL/Iバッチプログラムのサンプルコードを見てほしい。大文字ベースで記述された、実務にそのまま使える品質のコードだ。
- モジュール名: VARYINGサンプルバッチ
- 概要: VSAM(KSDS)ファイルに対し、SCALARVARYING属性を指定して
- 可変長文字列を安全に読み書きするメインフレーム標準パターン
TEST_VARYING: PROC OPTIONS(MAIN);
— レコード構造体の定義 —
- 顧客IDと可変長の顧客名を保持する構造体
DCL 1 CUST_REC,
5 CUST_ID CHAR(5), 顧客ID(固定長)
5 CUST_NAME CHAR(100) VARYING; 顧客名(可変長)
— ファイル定義 (ENVIRONMENTにSCALARVARYINGを指定) —
- これにより、CUST_NAMEの先頭2バイトの長さ情報が
- レコード入出力時に正しく担保される
DCL CUSTFILE FILE RECORD
ENV(FB BLKSIZE(4000) SCALARVARYING);
DCL EOF_FLG CHAR(1) INIT(‘0’);
— エラー処理用ONユニット —
ON ENDFILE(CUSTFILE)
EOF_FLG = ‘1’;
ON ERROR
BEGIN;
PUT SKIP EDIT (‘ SYSTEM ERROR OCCURRED ‘) (A);
- 現場の鉄則:異常時はダンプを取得して即座に異常終了させる
CALL PLIDUMP(‘TB’, ‘C’);
EXIT;
END;
— ファイルオープン (出力モード) —
OPEN FILE(CUSTFILE) OUTPUT;
— テストデータの書き込み —
CUST_ID = ‘A0001’;
CUST_NAME = ‘IBM JAPAN SYSTEMS’; 実際のデータ長は17バイト
WRITE FILE(CUSTFILE) FROM(CUST_REC);
CUST_ID = ‘A0002’;
CUST_NAME = ‘TOKYO MAINEC’; 実際のデータ長は12バイト
WRITE FILE(CUSTFILE) FROM(CUST_REC);
CLOSE FILE(CUSTFILE);
— ファイルオープン (入力モード・再確認) —
OPEN FILE(CUSTFILE) INPUT;
PUT SKIP EDIT (‘— 読込結果開始 —‘) (A);
DO WHILE (EOF_FLG = ‘0’);
READ FILE(CUSTFILE) INTO(CUST_REC);
IF EOF_FLG = ‘1’ THEN LEAVE;
- LENGTHブルトイン関数で現在の実際の長さを取得
PUT SKIP EDIT (
‘ID: ‘, CUST_ID,
‘ / NAME: ‘, CUST_NAME,
‘ / 保持長: ‘, LENGTH(CUST_NAME)
) (A, A, A, A, A, F(4));
END;
PUT SKIP EDIT (‘— 読込結果終了 —‘) (A);
CLOSE FILE(CUSTFILE);
RETURN;
END TEST_VARYING;
コードの解説とシニアエンジニアからのアドバイス
1. `ENV(FB BLKSIZE(4000) SCALARVARYING)` の指定
ここが今日の最大のポイントだ。レコード長が固定(FB)であっても、構造体内部に `VARYING` 属性を持つメンバーが存在する場合、この `SCALARVARYING` を忘れると、他言語モジュールとのデータ受け渡し時に「名前が途中から文字化けする」「先頭の2バイトがゴミデータになる」という現象が発生する。
2. `LENGTH` ビルトイン関数の活用
PL/Iでは、可変長文字列の現在の長さを取得するために `LENGTH(変数名)` を使う。COBOLの `FUNCTION LENGTH()` のような煩雑さがないのがPL/Iの優れたところだ。この長さは、先頭の2バイト(バイナリハーフワード)の値を抽象化したものである。
—
4. 現場でありがちなトラブルとデバッグのコツ
マイグレーションや他言語連携の案件で、よく次のようなトラブルに遭遇する。
> 「PL/Iで書き出したVSAMファイルをC言語のプログラムで読んだら、文字列の先頭に変なバイナリゴミ(`x’0011’` など)が入っていて、まともに表示できない!」
【原因】
まさにこれが `SCALARVARYING` の罠だ。PL/Iは可変長文字列の長さを格納するために、データ本体の直前に2バイトの長さ領域を勝手にねじ込んでいる。C言語側はそれを考慮せず、構造体の先頭からそのまま文字列として読み込もうとしたため、長さの数値が文字コードとして解釈されて化けていたのだ。
【対策】
- 方針A(ファイルフォーマットを合わせる):
他言語側(CやCOBOL)が長さ接頭辞を解釈できない場合は、PL/I側であらかじめ `VARYING` を外し、最大長までのパディング(空白埋め)を行った固定長(CHAR(100))として定義し直して入出力する。
- 方針B(インターフェース仕様を死守する):
どうしても可変長で受け渡す必要がある場合は、インターフェース設計書に「先頭2バイトはS9(4) COMPの長さ領域」であることを明記させ、PL/I側で `ENVIRONMENT(SCALARVARYING)` を確実に付与してコンパイルする。
—
5. おわりに
レガシーシステムの保守・開発において、言語仕様の「暗黙の挙動」を理解しているか否かは、トラブルシューティングのスピードを何倍にも分ける。
「コンパイルが通ったから動くはずだ」という安易な考えで `SCALARVARYING` を軽視していると、本番稼働後のデータ破損や、他システムとの連携不良というクリティカルな障害を引き起こす。
先輩風を吹かすわけではないが、コードを書くときは常に「このメモリの裏側で、コンパイラはどうデータ配置を行っているか?」を頭に思い描くクセをつけてほしい。その確実な積み重ねこそが、君を真のメインフレーム・アーキテクトへと押し上げてくれるはずだ。
それでは、次のバッチ改修現場でも健闘を祈る!
