おい、新人くん。ちょっと手を止めて、こっちに来たまえ。
今、君が改修しているあの夜間バッチのVSAM更新プログラム、何だか嫌な予感がする動き方をしていないかい?「レコードが見つからない(KEY NOT FOUND)」とか、ファイル切替のタイミングで変なループに入ったりとかさ。
メインフレームの現場で何十年も飯を食っていると、PL/IのRECORD I/O、特にVSAMの`KEYED`アクセスと`SEQUENTIAL`アクセスの裏側で何が起きているかを理解しているかどうかで、トラブルシューティングのスピードが文字通り10倍変わってくるんだ。
今日のテーマは、「RECORDファイルにおけるKEYED/SEQUENTIALアクセスの内部制御」だ。
PL/Iの優美でありながら時に牙をむくデータ制御の深淵を、一緒に覗いてみるとしよう。
—
1. 予約語を持たないPL/Iの「懐の深さ」と、それに伴う罠
まず大前提として、PL/Iという言語のユニークな仕様について話しておこう。
C言語やJavaなんかと違って、PL/Iには厳密な意味での「予約語(Reserved Words)」が存在しない。
例えば、`READ`や`WRITE`、`KEY`といったキーワードであっても、コンテキスト(文脈)によって変数名として定義することが理論上は可能だ。(もちろん、そんな恐ろしいコードを書くプログラマは正気を疑うがね)
1
/ 悪い冗談のようなコード:KEYという名前の変数を宣言 /
DCL KEY FIXED BIN(31);
コンパイラは、前後の構文解析(パーシング)によって「これは命令文のキーワードだ」「これはプログラマが定義した識別子だ」と文脈判断している。この柔軟性がPL/Iの強みであると同時に、ファイル制御ステートメントを書く際には、コンパイラに誤解を与えないための厳格なコーディング標準が求められる理由でもある。
—
2. VSAMアクセスにおける「レコードポインタ」の二面性
さて、本題のVSAM(KSDSなど)に対するRECORD入出力だ。
ここを勘違いしているプログラマが実に多い。
PL/Iでファイルをオープンし、RECORD I/Oを行う際、アクセスモードには大きく分けて以下の2つがある。
1. SEQUENTIALアクセス: ファイルの物理的(あるいは論理的なキー順)な並びに従って、順次レコードを読み書きするモード。
2. KEYEDアクセス: `KEY`オプションを指定し、特定のキー値を持つレコードを直接(ダイレクトに)読み書きするモード。
ここで重要なのは、「アクセスモードによって、内部のレコードポインタ(Current Position Indicator)の管理方法がガラリと変わる」という点だ。
シーケンシャル読みの内部挙動
`READ FILE(MYFILE) INTO(REC_BUF);` のようにキーを指定しない場合、VSAMのアクセスアーム(概念的なものだがね)は次に処理すべきレコードを指し示す。連続して実行すれば、パスポートコントロールのように次々とレコードを舐めていく。
キー指定(KEYED)読みの内部挙動
一方で、`READ FILE(MYFILE) INTO(REC_BUF) KEY(TARGET_KEY);` を実行した瞬間、内部ポインタは強制的にそのキーの位置へワープ(位置付け)される。
ここでベテランが必ず意識しなければならないのは、「KEYEDアクセスで位置付けを行った後の次のSEQUENTIAL読みは、そのワープした地点の次から始まる」というVSAMのカーソル挙動だ。この仕様を知らないと、「ランダムに読んだつもりが、その直後の順次読みで同じレコードを二重処理した」なんていう笑えないバグを生むことになる。
—
3. 実践!堅牢なKEYED/SEQUENTIAL混在処理プログラム
百聞は一見にしかずだ。実際のメインフレームの現場で通用する、実用的なPL/Iのサンプルコードを見せよう。
KSDS(Key-Sequenced Data Set)に対し、特定のマスターキーでダイレクトアクセス(KEYED)を行い、ヒットしたらそこから連続して数件を順次(SEQUENTIAL)処理する、よくある基幹バッチのパターンだ。
1
/ ================================================================= /
/ プログラム名: VSMSRCH1 /
/ 概要: VSAM(KSDS)ファイルに対するKEYED/SEQUENTIAL混在アクセスの例 /
/ ================================================================= /
VSMSRCH1: PROC OPTIONS(MAIN);
/ — ファイル定義 — /
DCL MSTRFILE FILE RECORD SEQUENTIAL
ENVIRONMENT(VSAM
Organization(KEYED)
Access(DIRECT)); / 読み替え用にDIRECT/SEQUENTIALを指定 /
/ — 変数定義 — /
DCL 1 MSTR_REC,
5 MSTR_KEY CHAR(8), / 検索キー (PK) /
5 MSTR_DATA CHAR(192); / データ本体 /
DCL W_TARGET_KEY CHAR(8) INIT(‘AB123456’);
DCL W_EOF_FLG BIT(1) INIT(‘0’B);
DCL W_LOOP_CNT FIXED BIN(31) INIT(0);
/ — 組み込み関数と条件(ON)の定義 — /
/ KEY(x)指定時にレコードが存在しない場合のトラップ /
ON KEY(MSTRFILE)
BEGIN;
PUT SKIP LIST(‘ 警告: 指定されたキーのレコードが見つかりません: ‘ || W_TARGET_KEY);
W_EOF_FLG = ‘1’B;
END;
/ 通常のファイル終了(EOF)トラップ /
ON ENDFILE(MSTRFILE)
BEGIN;
PUT SKIP LIST(‘ 情報: ファイルの終端に達しました。’);
W_EOF_FLG = ‘1’B;
END;
/ — ファイルオープン — /
OPEN FILE(MSTRFILE) INPUT;
PUT SKIP LIST(‘— 処理開始: KEYEDアクセスによる位置付け —‘);
/ 1. KEYEDアクセスによるダイレクト位置付けと読み込み /
READ FILE(MSTRFILE)
INTO(MSTR_REC)
KEY(W_TARGET_KEY);
/ ON-UNITでエラー(KEY NOT FOUND)が起きなければここを通る /
IF ^W_EOF_FLG THEN DO;
PUT SKIP LIST(‘【ヒット】初期キーレコード取得成功: ‘ || MSTR_REC.MSTR_KEY);
W_LOOP_CNT = W_LOOP_CNT + 1;
/ 2. ここから先はSEQUENTIALアクセスに切り替え(内部ポインタの継続利用) /
PUT SKIP LIST(‘— 続くレコードの順次(SEQUENTIAL)読み込み開始 —‘);
DO WHILE (W_LOOP_CNT < 5 & ^W_EOF_FLG); / KEYを外して読むことで、直前の位置から順次読みを行う / READ FILE(MSTRFILE) INTO(MSTR_REC); IF ^W_EOF_FLG THEN DO; PUT SKIP LIST(' [順次取得] 連続レコードキー: ' || MSTR_REC.MSTR_KEY); W_LOOP_CNT = W_LOOP_CNT + 1; END; END; END; / --- ファイルクローズ --- / CLOSE FILE(MSTRFILE); PUT SKIP LIST('--- 処理正常終了 処理件数 = ' || W_LOOP_CNT || ' ---'); RETURN; END VSMSRCH1; ---
4. デバッグとONユニット制御の要点
このコードを見て、新人くん、何か気づいたかい?そう、PL/Iの真骨頂である `ON KEY` 組み込み条件(Condition) の使い方だ。
C言語やCOBOLであれば、ファイル入出力のステータスコード(VSAMならFILE STATUSやRETCODE)を変数で受け取り、毎回 `IF STATUS = ’23’` のように泥臭く判定しなければならない。
しかし、PL/Iでは `ON KEY` や `ON ENDFILE` といった例外処理(ONユニット)を定義しておくことで、ハードウェア/アクセスメソッド層で検知された例外をスマートにトラップできる。
実務における重要な注意点:
1. ONユニットのスコープ
ONユニットは動的に有効範囲が決まる。サブルーチンを呼び出す際などにスコープの切れ目を意識しないと、予期せぬ上位のONユニットが発火してデバッグに何時間も費やす羽目になる。
2. KEYEDからSEQUENTIALへの移行時の注意(重複キー)
上のコードではKSDSを想定しているが、もしこれがAIX(Alt-Index)や、重複キーを許す定義になっている場合、`KEY` 指定による読み込みの挙動が少し複雑になる。重複キーの先頭をヒットさせた後、順次読みで残りのデュプリケートレコードをどう拾うか、アクセスパスの定義と合わせてJCL側のDD記述(AMORGなど)も確認が必要だ。
—
今日の解説はここまでだ。
レガシーシステムの寿命は、私たちが書くこういった「目に見えない内部制御の正確さ」に支えられている。何となく動くコードではなく、「なぜコンパイラがこのコードをこう解釈し、OSがどうファイルを動かしているか」を頭に描きながらコードを書く。
それができるようになれば、君も一人前のメインフレーム・アーキテクトだ。
さて、コーヒーでも飲んで、次のテストデータの準備に取り掛かろうか。
