索引レコード I/O

Open PL/I の索引 I/O (仮想記憶アクセス方式 (Virtual Storage Access Method; VSAM) とも呼ばれる) では、ファイルのレコードに格納される文字列キーを使用します。各レコードに 1 つのキーが含まれ、キーはファイルに関連付けられている索引で辞書順に並べられます。レコードには、目的のレコードのキーを指定してランダムにアクセスするか、キーの順序に従って順番にアクセスするか、これらの方法を組み合わせてアクセスすることができます (次の表を参照)。

ファイル属性 許可されるアクセス
SEQUENTIAL SEQUENTIAL のみ
DIRECT KEYED のみ
KEYED SEQUENTIAL      SEQUENTIAL または KEYED、あるいはその両方の組み合わせ

INDEXED (VSAM と同等) ファイルは、ENVIRONMENT 属性の INDEXED または VSAM オプションを含めることで宣言します。次に例を示します。

DECLARE MASTER RECORD OUTPUT FILE 
   ENVIRONMENT(VSAM KEYLOC(1) 
   KEYLENGTH(12) RECSIZE(130));

KEYLOC オプションは、レコードにあるキーの先頭バイトを KEYLOC(1) としてカウントして、その先頭バイトのバイト位置を指定します。KEYLENGTH オプションは、キーの文字数を指定します。RECSIZE は、レコード長を指定するか、可変長レコードのファイルの場合は最大レコード長を指定します。

実際には、ファイルの RECSIZE、KEYLOC、および KEYLENGTH を指定する必要があるのは、そのファイルの作成時だけです。既存の索引ファイルの読み取りまたは更新を行うプログラムでは、これらのオプションを含めることもできますが、その必要はありません。Open PL/I ではこれらの特性を既存のファイルから判別できます。完全な IBM モードを使用している場合は、ENVIRONMENT 属性で RECSIZE、KEYLOC、または KEYLENGTH を指定する必要はなく、出力用に VSAM ファイルを開くときにはファイル ヘッダー情報が使用されます。

ENVIRONMENT 属性ではさまざまなオプションを指定できます。これらのオプションを次の表に示します。この表には、Open PL/I では必要ない、またはサポートされていないオプションもいくつか示しています。ユーザーが他のコンパイラの使用経験から、これらのオプションも含まれていると想定する可能性があるためです。

オプション
説明
ASCII Open PL/I では ASCII オプションが前提となっています。
BKWD SEQUENTIAL モードでの索引ファイル読み取りのバックワード処理を指定します。つまり、順次処理がファイル内の現在のレコード (デフォルトでは最後のレコード) から開始され、前のレコードに進みます。
BUFFERS Open PL/I では BUFFERS オプションは必要ありません。使用した場合、コンパイラで無視されます。
BUFOFF Open PL/I では BUFOFF オプションは必要ありません。使用した場合、コンパイラで無視されます。
BUFND Open PL/I では BUFND オプションは必要ありません。使用した場合、コンパイラで無視されます。
BUFNI Open PL/I では BUFNI オプションは必要ありません。使用した場合、コンパイラで無視されます。
BUFSP Open PL/I では BUFSP オプションは必要ありません。使用した場合、コンパイラで無視されます。
CONSECUTIVE 連続編成のファイルを指定します。関連情報については、『Open PL/I ユーザー ガイド』の -defext コンパイラ オプションの説明を参照してください。
CTLASA CTLASA オプションは無視されます。
CTL360 Open PL/I では CTL360 オプションは実装されていません。使用した場合、コンパイラで警告が発行されます。
EBCDIC EBCDIC データが存在することを指定します。
F | FB | FS | FBS レコード形式を指定します。ここで、F は固定長、B はブロック化、S は標準です。Open PL/I では、このような指定は F のみ (固定長) が設定されている場合と同じように扱われます。
GENKEY 総称キー (キーのプレフィックスである文字列) を指定します。READ に KEY 句を指定すると、KEY 句で指定した文字列以上のキーを持つ最初のレコードが返されます。後続の順次 READ (KEY 句を指定しない) では、現在のレコードよりも大きい次のレコードが返されます。
GRAPHIC GRAPHIC データが存在することを指定します。
INDEXAREA(n) Open PL/I では INDEXAREA オプションは必要ありません。使用した場合、コンパイラで無視されます。
INDEXED 索引編成のファイルを指定します。関連情報については、『Open PL/I ユーザー ガイド』の -defext コンパイラ オプションの説明を参照してください。
KEYLENGTH(n) 索引ファイルの埋め込みキーの長さ n を指定します。KEYLENGTH を指定できるのは、INDEXED または VSAM を指定した場合だけです。
KEYLOC(n) レコードにおける埋め込みキーの開始位置を指定します。n は次の制限の範囲内で指定する必要があります。

1 ≥ n ≥ recordsize – keylength + 1

KEYLOC のデフォルト値は 1 です。

KEYLOC を指定できるのは、INDEXED または VSAM を指定した場合だけです。

注:

KEYLOC では、SCALARVARYING オプションの指定によって生成される 2 バイトのプレフィックスを考慮するようにしてください。

LEAVE Open PL/I では LEAVE オプションは必要ありません。使用した場合、コンパイラで無視されます。
MINRECSIZE(n) 最小レコード長 n を指定します。n は整数にする必要があります。このオプションを使用すると、ディスク領域の使用率が向上します。MINRECSIZE オプションは V (可変長) レコード形式で必要となります。F (固定長) レコード形式の場合は無視されます。KEYLOC オプションおよび KEYLENGTH オプションで指定する値は、MINRECSIZE オプションで指定する値の範囲内にする必要があります。MINRECSIZE オプションの詳細については、下記を参照してください。
NCP(n) Open PL/I では NCP オプションは適用されません。Open PL/I では INDEXED ファイルは VSAM ファイルと同じように扱われます。
NOWRITE Open PL/I では NOWRITE オプションは必要ありません。使用した場合、コンパイラで無視されます。
PASSWORD(pwd) Open PL/I では PASSWORD オプションは実装されていません。使用した場合、コンパイラで警告が発行されます。
RECSIZE(n) 最大レコード長 n を指定します。n は正の整数か、初期化子を含む静的変数にする必要があります。静的変数の場合は、初期値が最大固定レコード サイズとして使用されます。「動的な」RECSIZE の使用法はサポートされていないため、これが不要な場合は、STATIC INIT よりも VALUE の方が適しています。
REGIONAL(1 | 2 | 3) REGIONAL(1) オプションは、レコードが領域番号で識別されるファイルを指定します。

Open PL/I では REGIONAL(2) オプションおよび REGIONAL(3) オプションは実装されていません。使用した場合、コンパイラでエラーとしてフラグが設定されます。

REUSE REUSE オプションは、VSAM データ セットに関連付けられた OUTPUT ファイルを作業ファイルとして使用するように指定します。
SCALARVARYING 可変長レコードの入出力に使用されます。SCALARVARYING を使用すると、レコードの現在の長さを指定する 2 バイトのプレフィックスが認識されるようになります。
注:

KEYLOC では、SCALARVARYING オプションの指定によって生成される 2 バイトのプレフィックスを考慮するようにしてください。

SIS Open PL/I では SIS オプションは必要ありません。使用した場合、コンパイラで無視されます。
SKIP Open PL/I では SKIP オプションは必要ありません。使用した場合、コンパイラで無視されます。
TP(MIR) Open PL/I では TP(MIR) オプションは実装されていません。使用した場合、コンパイラでエラーとしてフラグが設定されます。
TRKOFL Open PL/I では TRKOFL オプションは必要ありません。使用した場合、コンパイラで無視されます。
U | D | DB レコード形式を指定します。Open PL/I では、このような指定は F (固定長) が設定されている場合と同じように扱われます。
V | VB | VS | VBS レコード形式を指定します。ここで、V は可変長、B はブロック化、S はスパンドです。Open PL/I では、このような指定は V が設定されている場合と同じように扱われます。
VSAM 仮想記憶アクセス方式 (VSAM) 用に編成されたファイルを指定します。関連情報については、『Open PL/I ユーザー ガイド』の -defext コンパイラ オプションの説明を参照してください。

INPUT 属性または UPDATE 属性によって開かれた索引 (VSAM) ファイル内のレコードのランダム アクセス読み取りを行うには、KEY オプションを指定した READ 文を使用します。次に例を示します。

READ FILE(PARTS) INTO(PART_REC) KEY(PART_NUM);

順次読み取りでは、キーの順序を使用して、ファイルで最後に読み取ったレコードの次にあるレコードを読み取るか、前に読み取ったレコードがない場合はファイルの最初のレコードを読み取ります。順次 READ 文の場合、KEY オプションは省略します。

UPDATE 属性によって開かれたファイルからレコードが既に読み取られていて、他の I/O 操作が間で実行されていない場合は、キーを指定せずに REWRITE を実行できます。この場合、既に読み取ったレコードは新しい値で上書きされます。次に例を示します。

READ FILE(PARTS) INTO(PART_REC) KEY(PART_NUM); 
PART_REC = NEW VALUE;
REWRITE FILE(PARTS) FROM(PART_REC);

ただし、索引ファイルのレコードをランダムに更新するには、REWRITE 文で KEYFROM オプションを指定します。次に例を示します。

REWRITE FILE(PARTS) FROM(PART_REC) KEYFROM('12XJ04');

索引ファイルを OUTPUT 属性または UPDATE 属性によって開いた場合は、KEYFROM オプションを指定した WRITE 文を使用してファイルに新しいレコードを追加できます。KEYFROM を指定せずに WRITE 文を実行すると、ファイルに新しいレコードが順番に追加されます。

DELETE 文を使用すると、UPDATE 属性によって開かれた索引ファイルからレコードを削除できます。レコードを削除するには KEY オプションを使用する必要があります。

索引 (VSAM) ファイルで使用するキーはすべて CHARACTER 型である必要があります。KEY オプションまたは KEYFROM オプションが CHARACTER 型以外の値を参照している場合、その値は I/O 操作が開始される前に CHARACTER に変換されます。PICTURE 型のキーが CHARACTER に変換される場合、キーの内容は実際には変更されません (「データ型変換」の章を参照)。

索引 (VSAM) ファイルには、固定長レコードまたは可変長レコードを含めることができます。レコードは、ファイルの作成時に ENVIRONMENT 属性で F オプションを使用した場合は固定長で、V オプションを使用した場合は可変長です (上記の表を参照)。F も V も指定していない場合は、F と想定されます。

固定長レコード ファイルでは、すべてのレコードの長さが同じである必要があります。この長さは、ファイルの作成時に ENVIRONMENT 属性の RECSIZE オプションで指定した長さです。可変長レコード ファイルの場合は、RECSIZE でファイルにおいて許可される最大レコード長を指定します。可変長レコード ファイルでは MINRECSIZE オプションも指定する必要があります。この値は、ファイル内のすべてのレコードに最低限必要な長さを指定します。ファイルのキーは、ファイルのこの最小限の部分に含まれている必要があります。

SCALARVARYING オプションでは、データ ファイル内のすべてのレコードに 2 バイトのプレフィックスがあることを指定します。ユーザーは、KEYLOC の値を指定するときにこのことを考慮する必要があります。KEYLOC の値は、1 から始まるレコードの絶対バイト位置です。このオプションは、レコード形式が F または V の場合に使用できます。これにより、I/O 文で文字可変レコードをより一般に使用できるようになりますが、文字レコードを送信することもできます。文字可変項目を WRITE 文または REWRITE 文で使用する場合、SCALARVARYING オプションが設定されていると、レコードがデータ ファイルに書き込まれるときに 2 バイトのプレフィックスはレコードとともに保持されます。SCALARVARYING オプションを設定しない場合、プレフィックスは書き込まれません。この場合、現在の長さを超えるレコードの残りの部分は不定長になります。文字レコードが書き込まれるとき、SCALARVARYING オプションが設定されている場合はプレフィックスが追加されます。SCALARVARYING オプションを使用すると、RECORD 条件を発生させずに、最大レコード サイズよりも小さいレコードの書き込みおよび読み取りを実行できます。これは、位置指定モードの I/O を実行する際にも役立ちます。

SCALARVARYING オプションの使用時に KEYLOC を設定する際は、2 バイトのプレフィックスを「補正」することが非常に重要です。

V タイプのレコードを使用すると、一部のレコードを送信できます。Open PL/I では、このサポートのためにレコードに制御バイトを含める必要はありません。ランタイム システムによって、送信されるバイト数に関する情報が自動的に保持されます。レコードに制御情報を含める必要がないため、ユーザーがレコード タイプに基づいて KEYLOC を変更することはありません。KEYLOC の位置に影響するのは SCALARVARYING オプションだけです。

PL/I 言語には 1 つのファイルに複数のキーを指定できるメカニズムは用意されていませんが、Open PL/I では複数のキーを介してデータにアクセスできます。1 つのキー (主キーと呼ばれる) を使用して索引ファイルを作成した後、ユーティリティ Ipivsam を使用してファイルに索引 (つまり、キー) を追加できます。PL/I プログラムで、データの異なる各キー バージョンを個別のファイルとして宣言する必要があります。ただし、宣言したすべてのファイルで、実際には同じデータ セットを参照することができます。次に例を示します。

DECLARE EMPFILE1 RECORD UPDATE FILE
   ENV(VSAM KEYLOC(1) KEYLENGTH(6) RECSIZE(145));
DECLARE EMPFILE2 RECORD UPDATE FILE
   ENV(VSAM KEYLOC(31) KEYLENGTH(3) RECSIZE(145) GENKEY); 
DECLARE 1 EMP_ 
   2 EMP_ID       (CHAR(6),
   2 NAME         (CHAR(24),
   2 DEPARTMT     (CHAR(3),
   ...
  
OPEN FILE(CUSTFILE1) TITLE('employee'); 
OPEN FILE(CUSTFILE2) TITLE('employee'); 
                        /* same file, different key */

次のサンプル プログラムは、VSAM 編成のファイルの I/O 用に ENVIRONMENT 変数とそのオプションを使用する例を示しています。

/* Example of VSAM I/O */

TEST: PROCEDURE OPTIONS(MAIN);

   %REPLACE TRUE BY '1'B; %REPLACE FALSE BY '0'B;

   DECLARE DB FILE ENV(VSAM KEYLOC(21) KEYLENGTH(120) RECSIZE(244));
   DECLARE DBG FILE ENV(INDEXED GENKEY KEYLOC(21) 
      KEYLENGTH(120) RECSIZE(244));

   DECLARE 1 DB_RECORD,
      2 HEADER     CHAR(20),
      2 DBKEY      CHAR(120),
      2 STUFF      CHAR(100),
      2 NUMBER     FIXED BIN(31);

   DECLARE I FIXED BIN(31);
   DECLARE CS CHAR(26) STATIC
                     INIT('THEQUICKBROWNFXJMPSVLAZYDG'); 
   DECLARE C(26) CHAR DEFINED(CS);
   DECLARE LETTERS CHAR(30);

   /* CREATE DB */
   OPEN FILE(DB) RECORD KEYED SEQUENTIAL OUTPUT 
               TITLE('MYFILE1');
   HEADER = 'DATA RECORD HEADER';
   STUFF = COPY('ABCDE',20);
   DO I = 1 TO 26; 
      NUMBER = I; 
      DBKEY = '@'||C(I) ||COPY('@',118);
      WRITE FILE(DB) FROM(DB_RECORD) KEYFROM(DBKEY);
      END;
   CLOSE FILE(DB);

   /* TRY A GENERIC READ THEN MOVE SEQUENTIALLY THRU THE FILE */
   OPEN FILE(DBG) RECORD KEYED SEQUENTIAL INPUT
               TITLE('MYFILE1'); 
   ON ENDFILE(DBG) GOTO EXIT_1; 
   READ FILE(DBG) KEY('@') INTO(DB_RECORD);
   LETTERS = SUBSTR(DBKEY,2,1); 
   DO WHILE(TRUE);
      READ FILE(DBG) INTO(DB_RECORD);
      LETTERS = TRIM(LETTERS) ||SUBSTR(DBKEY,2,1);
      END;
EXIT_1:
   CLOSE FILE(DBG);
   PUT EDIT(LETTERS)(A);
   IF LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' THEN
      PUT SKIP LIST('TEST PASSES'); 
   ELSE PUT SKIP LIST('TEST FAILS');

   PUT SKIP; 
END TEST;