こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいは従来型のビジネス言語の経験がある方にとって、IBMメインフレームの「PL/I(ピーエルワン)」は、最初少しだけ独特の要塞のように見えるかもしれません。
「なんだか変数名のルールが緩い気がする……」
「可変長文字列の裏側で何が起きているの?」
そんな不安を抱えていらっしゃるあなたへ。大丈夫です、怖くありませんよ。一つずつ蓋を開けて中身を見ていけば、PL/Iがいかに合理的で、かつ少しユニークな思想で作られているかがよく分かります。
今回は、PL/Iのデータ制御の肝である「識別子の命名規則の秘密」に少し触れたあと、本日のメインテーマである「ENVIRONMENT属性のSCALARVARYING指定」について、JavaやCOBOLとのデータ互換性の視点も交えながら、じっくりと紐解いていきましょう。
—
1. 予約語を持たない?PL/Iのちょっと変わった世界観
他のプログラミング言語(JavaやCOBOLなど)では、`IF`や`THEN`、`READ`といったキーワードは「予約語(Keyword)」と呼ばれ、変数の名前として使うことは絶対に許されませんよね。「変数名を `IF` にしたい」なんて言ったら、コンパイラに怒られてしまいます。
しかし、PL/Iの設計思想は少し違います。なんと、PL/Iには「厳密な意味での予約語」がほとんど存在しません。
例えば、こんなコードが書けてしまいます。
DECLARE IF FIXED BINARY(31); / 変数名に IF という名前をつけちゃった! /
IF = 10; / 変数 IF に 10 を代入 /
「えっ、コンパイラはどうやって `IF` が条件分岐の `IF` なのか、変数なのかを判断しているの?」と驚きですよね。
PL/Iのコンパイラは非常に賢く、前後の文脈(Context)から「ここはキーワードだな」「ここは変数名だな」と空気を読んで判断しています。これをコンパイラ界の「阿吽の呼吸」とでも呼びましょうか。
ただし、現場の保守性を考えると、こういうトリッキーな命名はバグの元ですので絶対にやめましょうね(笑)。「そういう柔軟で、ある意味大らかなルールを持つ言語なんだな」と心にとどめておいてください。
—
2. 本日の本丸:可変長文字列と「SCALARVARYING」の正体
さて、ここからが本題です。基幹システムのマイグレーションや、他言語(JavaやC言語、COBOLなど)とのファイル連携で最もハマりやすい罠、それが「可変長文字列(VARYING)」のメモリ上の持ち方です。
文字データを扱うとき、固定長(CHAR)ではなく、長さが変動する可変長(VARYING)を使うことはよくありますよね。PL/Iで可変長文字列を宣言すると、コンパイラは裏側でその文字列の「実際の長さ」をどこかに保持しなければなりません。
ここで登場するのが、今回のテーマである `ENVIRONMENT(SCALARVARYING)` です。
2.1 メモリ上で何が起きているのか?
PL/Iで可変長文字列(`CHAR(100) VARYING` など)を定義した場合、ストレージ(メモリやファイル上のデータ領域)の先頭はどうなっているでしょうか?
実は、PL/Iのデフォルトの挙動では、データの先頭2バイトに「2進数(Binary)の長さ」が格納される仕様になっています。
イメージしてみましょう。
「IBM」という3文字の可変長文字列をPL/Iで保持させたとします。
[ 2バイトの長さ領域 ][ 実際の文字列データ (最大100バイト) ]
[ 0x0003 ][ ‘I’, ‘B’, ‘M’, 100バイト分のパディング… ]
先頭の2バイトには、文字列の長さである「3(16進数で `0003`)」がビッグエンディアン(上位バイトが先)で入ります。その直後に、実際の文字データである `I` `B` `M` が続きます。
2.2 他言語(JavaやCOBOL)連携での大問題
ここで、Javaで書かれたオープン系のバッチプログラムや、COBOLのファイルレイアウトを思い出してください。
- COBOLの場合: 可変長(OCCURS DEPENDING ON など)の仕組みはありますが、基本は固定長か、あるいは別の制御方式を取ります。
- Javaの場合: `String` 型やバイト配列に、先頭2バイトの長さプレフィックスなど自動的にはついてきません。
もし、PL/I側で作成した可変長データを、何の配慮もなしにJavaやCOBOLのプログラムで読み込もうとすると……?
「あれ? 最初の2文字が化けてる!? データがズレる!」 という大惨事が発生します。Java側は、先頭の2バイトを文字データ(文字コード)の一部として解釈してしまうからです。
2.3 ENVIRONMENT(SCALARVARYING) の役割
ここで救世主となるのが、ファイル入出力(OPEN / READ / WRITE)の際に指定する `ENVIRONMENT(SCALARVARYING)` 属性です。
このオプションは、コンパイラやファイルアクセスに対して次のように伝えます。
> 「このファイルに含まれる可変長スカラーデータには、先頭に2バイトの長さフィールド(Prefix)が付与されているんだよ。入出力のときにちゃんとよしなに解釈してね!」
逆に、他言語(例えば、先頭2バイトの長さを持たない別のシステム形式)とデータをやり取りするファイルを作る場合、この挙動を意識しないとデータの互換性が完全に崩れてしまいます。
実際のコード例を見てみましょう。
/ —————————————————————- /
/ プログラム名: VARYING01 /
/ 概要: 可変長文字列をファイルに出力する際のデータ構造定義の例 /
/ —————————————————————- /
VARYING_DEMO: PROC OPTIONS(MAIN);
/ 1. 可変長文字列を含む構造体の定義 /
DECLARE 1 CUST_REC,
/ 氏名:最大50バイトの可変長文字列 /
5 CUST_NAME CHAR(50) VARYING,
/ 住所:最大100バイトの可変長文字列 /
5 CUST_ADDR CHAR(100) VARYING;
/ 2. ファイルの定義 /
/ ENVIRONMENT(SCALARVARYING) を指定することで、 /
/ 可変長項目の先頭2バイト長フィールドの扱いを明示しています。 /
DECLARE OUT_FILE FILE RECORD
OUTPUT
ENVIRONMENT(SCALARVARYING);
/ ファイルのオープン /
OPEN FILE(OUT_FILE) DATASET(‘USER.CUST.DATA’)
ENV(F(200)); / 固定長ブロックなどの物理属性 /
/ データの代入(この時、裏側で先頭2バイトに文字長がセットされます) /
CUST_NAME = ‘山田 太郎’;
CUST_ADDR = ‘東京都千代田区永田町1-7-1’;
/ ファイルへの書き出し /
WRITE FILE(OUT_FILE) FROM(CUST_REC);
/ クローズ /
CLOSE FILE(OUT_FILE);
END VARYING_DEMO;
—
3. レガシー移行・実務の現場からのアドバイス
JavaやCOBOLからメインフレームの世界に飛び込んだエンジニアが、この `SCALARVARYING` や可変長データの仕様でつまずくポイントは、だいたい決まっています。
1. 「ファイルビューアで見たら文字化けするんだけど!」問題
- → メインフレームのユーティリティ(ISPF/PDFのブラウズなど)で直接データセットを覗いたとき、先頭の2バイトがバイナリ(制御文字や謎の記号)で見えるのは、この長さプレフィックスがあるからです。故障ではありません、仕様です!安心してください。
2. マイグレーション時のデータコンバージョン
- → もし将来的にメインフレーム上のこのファイルを、クラウド上のJava(RDBやS3など)に移行する場合、先頭の2バイト(長さフィールド)を削る、あるいは適切な文字列型(VARCHAR)に変換するアンパック処理が絶対に必要になります。移行設計の際は、ここを必ずチェックリストに入れましょう。
—
まとめ
いかがでしたでしょうか?
PL/Iの「予約語を持たない自由な命名規則」や、可変長文字列の裏側にある「先頭2バイトの長さ保持」、そしてそれを制御する `ENVIRONMENT(SCALARVARYING)` の仕組み。
一見するとレガシー特有のクセの強い仕様に見えますが、データ構造のメモリ配置の理屈さえ分かってしまえば、決して怖いものではありません。むしろ、ハードウェアの制約の中で最大限の効率を絞り出した、先人たちの知恵の結晶です。
メインフレームやPL/Iのモダナイゼーション、あるいは他言語との連携で迷ったときは、ぜひこの「裏側の2バイト」を思い出してみてくださいね。
それでは、快適なメインフレーム・ライフを!
