Previous | Contents | Index |
You can also specify the extension size by using the DCL command SET FILE/EXTENSION=n. To determine the value to be assigned to the EXTENSION attribute, multiply the record size by the number of records to be added.
If you do not specify an extension size, RMS allocates the number of
blocks needed for each extension.
6.6.3 Using Relative Files
You can create a relative file from any existing file that is suitable for relative file organization. Example 6-1 illustrates copying a sequential file with fixed-length records into a relative file.
The following notes are keyed to Example 6-1:
Example 6-1 Creating a Relative File |
---|
COPY_TO_RELATIVE: PROCEDURE OPTIONS(MAIN); %INCLUDE PARTLIST; /* Declaration of PARTLIST */ /* (1) */ DECLARE OLDFILE FILE INPUT RECORD SEQUENTIAL; DECLARE RECORD_NUMBER FIXED BINARY; DECLARE PARTS FILE OUTPUT KEYED RECORD ENVIRONMENT( MAXIMUM_RECORD_NUMBER(600), /* (2) */ FIXED_LENGTH_RECORDS, MAXIMUM_RECORD_SIZE(36)); ON ENDFILE(OLDFILE) BEGIN; /* (3) */ CLOSE FILE(OLDFILE), FILE(PARTS); STOP; END; OPEN FILE(OLDFILE), FILE(PARTS); DO WHILE('1'B); READ FILE(OLDFILE) INTO(PARTLIST); /* (4) */ RECORD_NUMBER = PARTLIST.NUMBER; WRITE FILE(PARTS) FROM(PARTLIST) KEYFROM(RECORD_NUMBER); /* (5) */ END; END COPY_TO_RELATIVE; |
Records in this file can subsequently be accessed either sequentially or by part number. To access a record by part number, you specify the number as a key. For example:
GET LIST(INPUT_NUMBER) OPTIONS(PROMPT('Part? ')); READ FILE(PARTS) INTO(PARTLIST) KEY(INPUT_NUMBER); |
Here, the value entered in response to the GET statement is used as a key value to access a record in the file.
In Example 6-1, the file PARTS is opened with the KEYED and OUTPUT attributes. When this program is executed, the amount of space allocated for the file PARTS depends on the relative record numbers of the records that are written to the file. For example, if the largest record number allowed for any record in the file is 600, but the largest record number specified for a record is 200, then RMS allocates only enough space for 200 records.
When you initially populate a file and you plan to fill the entire file through the maximum record number, you can cause RMS to use either of the following techniques to allocate space for the entire file:
These techniques can optimize the throughput for the subsequent file additions, because RMS will not need to perform repeated extensions to the file as records are added.
To add or modify records in a relative file, open the file with the DIRECT and UPDATE attributes. For example, a procedure that updates the file PARTS when new stock is ordered might contain the following:
ORDER_PARTS: PROCEDURE (ORDERED_AMOUNT,PART_NUMBER); %INCLUDE PARTLIST; /* Declaration of PARTLIST */ DECLARE (ORDERED_AMOUNT,PART_NUMBER) FIXED BIN(15); DECLARE PARTS FILE RECORD DIRECT UPDATE; OPEN FILE(PARTS); READ FILE(PARTS) INTO(PARTLIST) KEY(PART_NUMBER); PARTLIST.ON_ORDER = PARTLIST.ON_ORDER + ORDERED_AMOUNT; REWRITE FILE(PARTS) FROM(PARTLIST); CLOSE FILE(PARTS); END; |
In this example, the procedure ORDER_PARTS receives as its parameters the order quantity and the part number. It reads the record associated with the part number from the file, adds the order quantity to the existing quantity, and rewrites the record.
Reading a Relative File Sequentially
You can access a relative file sequentially as well as by key. When you access the file sequentially, each READ statement returns the record in the next cell that contains a record, skipping empty cells. The following example illustrates reading a relative file sequentially:
PRINT_PART: PROCEDURE OPTIONS(MAIN); %INCLUDE PARTLIST; /* Declaration of PARTLIST */ DECLARE PARTS FILE, CHECK_NUM FIXED; DECLARE EOF BIT(1) ALIGNED INIT('0'B); OPEN FILE(PARTS) INPUT SEQUENTIAL RECORD KEYED; ON ENDFILE(PARTS) EOF = '1'B; READ FILE(PARTS) INTO(PARTLIST) KEYTO(CHECK_NUM); DO WHILE (^EOF); PUT SKIP EDIT(PARTLIST.NAME, /* Output data */ UNIT_PRICE, IN_STOCK, ON_ORDER) (A,X,A,X,A,X,A); PUT SKIP EDIT('Relative record number',CHECK_NUM, 'Part number:',PARTLIST.NUMBER) (X(10),A,X,F(5),A,X,A); /* Output verification */ READ FILE(PARTS) INTO(PARTLIST) KEYTO(CHECK_NUM); END; |
This procedure outputs the contents of the file PARTS, listing each
field in the data records described by PARTLIST. The READ statement
specifies the KEYTO option; the procedure outputs the value returned to
the variable CHECK_NUM.
6.6.4 Error Handling
PL/I signals the KEY condition when errors occur during the processing of record numbers for relative files. For example, it signals the KEY condition when a relative record number exceeds the maximum record number specified for the file, or when the number of a record that already exists is specified in a KEYFROM option in a WRITE statement.
The following sample ON-unit shows how to detect whether a record already exists in a relative file or whether a record number specified exceeds the file's maximum record number.
ON KEY(PARTS) BEGIN; %INCLUDE $RMSDEF; /* Check for duplicate records */ IF ONCODE() = RMS$_REX /* If duplicate */ THEN DO; PUT SKIP EDIT('Part number', PARTLIST.NUMBER,'exists. Reenter') (A,X,A,X,A); GET LIST(PARTLIST.NUMBER); /* Get new value */ GOTO GET_DATA; /* Go get other data */ END; /* Check for maximum record number exceeded */ ELSE IF ONCODE = RMS$_MRN THEN DO; PUT SKIP EDIT('Part number',PARTLIST.NUMBER, 'invalid. Reenter') (A,X,A,X,A); GET LIST(PARTLIST.NUMBER); /* Get new value */ GOTO GET_DATA; /* Go get other info */ END; END; . . . GET_DATA: |
In this example, the ON-unit sets symbolic names for two specific status values returned by ONCODE, from PLI$STARLET:
In an ON-unit for the KEY condition for a relative file, ONCODE can also return the values associated with the following status codes:
This section describes the organization of indexed sequential files,
suggests considerations for creating and using them, and shows examples
of some typical operations.
6.7.1 Indexed File Organization
In an indexed sequential file, the file contains data records and pointers to the records. Data records and record pointers are arranged in buckets, which consist of an integral number of physically contiguous 512-byte disk blocks. Individual records within the file are located by the specification of the keys (indexes) associated with the records. Each file must have a primary key-that is, a field within the record that has a unique value to distinguish it from all other records in the file. An indexed sequential file can also have up to 254 alternate keys, which need not have unique values.
RMS writes records to an indexed file in collating sequence according to the primary key, putting them in buckets that are chained together. Thus, an individual file can be accessed sequentially with any key.
Figure 6-2 illustrates an indexed sequential file with a single key.
Figure 6-2 An Indexed Sequential File
The records in the file illustrated in Figure 6-2 consist of address data that might have been defined in a PL/I structure as follows:
DECLARE 1 ADDRESS_FILE, 2 EMPLOYEE_NAME CHARACTER(30), 2 ADDRESS, 3 STREET CHARACTER(20), 3 ZIP_CODE CHARACTER(5); |
In this file, the key is the employee name.
When RMS writes records to an indexed sequential file, it builds and maintains a tree-like structure of key values and location pointers. When records are accessed by key, RMS uses the tree to locate individual records. Thus, when a PL/I program accesses the record whose key value is JONES, RMS traverses the indexes to locate the record.
When new records are added to an indexed sequential file, a data bucket
may not have enough room to accommodate a new record. In this case, RMS
performs what is called bucket splitting: it inserts a new bucket in
the chain of data buckets and moves enough records from the previous
bucket to preserve the primary key sequence. Bucket splitting is
transparent to the PL/I program; the program knows only that it has
added a record to the file.
6.7.2 Defining and Creating an Indexed Sequential File
As shown in Figure 6-3, an indexed sequential file must first be defined, then created. You define the characteristics of your indexed sequential file using the Open File Definition Language (FDL), and then use that definition (the resulting FDL file) to create the indexed sequential file for use by your program.
You can use several methods to create an FDL definition file: an OpenVMS editor, a PL/I program, or the FDL Facility. The following steps are keyed to the callout numbers in Figure 6-3.
Figure 6-3 Creating a Data File
The following sections explain how to create a definition file by using
the FDL Facility and how to create one through a PL/I program.
6.7.2.1 Using EDIT/FDL
You can use the command EDIT/FDL to define an indexed sequential file for PL/I. Note that while you can construct FDL file specifications using any of the OpenVMS text editors, the FDL Facility gives you the added convenience of systematic prompting for and automatic formatting of the specifications. The specifications are written in the File Definition Language (FDL) and the files are referred to as FDL files.
The FDL Facility is interactive: it prompts you to enter data and responds with error messages when you enter data incorrectly. It supplies many default values. The only data that you must specify is as follows:
You can obtain help by pressing RETURN in response to the first menu. Each item on the menu leads to another menu in the specification of the data file. You can return to the original menu by pressing Ctrl/z.
$ EDIT/FDL [Return] _File: STATE50 [Return] Parsing Definition File DBA0:[SMITH]STATE50.FDL; will be created. Press return to continue (^Z for Main Menu)[Return] OpenVMS FDL Editor Add to insert one or more lines into the FDL definition Delete to remove one or more lines from the FDL definition Exit to leave the FDL Editor after creating the FDL file Help to obtain information about the FDL Editor Invoke to initiate a script of related questions Modify to change existing line(s) in the FDL definition Quit to abort the FDL Editor with no FDL file creation Set to specify FDL Editor characteristics View to display the current FDL definition Main Editor Function (Keyword)[Help]:INVOKE [Return] |
FDL displays further menus to guide you through the creation of the file.
After you define your data file, FDL displays a message like the following, giving the file specification of the definition file:
DBA0:[SMITH]STATE50.FDL;1 40 lines |
FDL formats the information that you supply so that it can be used to create an indexed sequential file. However, you can also use EDT or any other OpenVMS text editor to design an FDL text.
FDL allows you to specify many characteristics for your file, but requires you to specify only a limited number. The following example, written in FDL, illustrates some of the possible file characteristics. The explanatory comments to the right would not appear in an FDL file; they have been added for your information.
SYSTEM SOURCE VAX/VMS FILE NAME state50.dat /* file name */ ORGANIZATION indexed RECORD CARRIAGE_CONTROL carriage_return FORMAT fixed /* record attributes */ SIZE 60 AREA 0 ALLOCATION 25 /* number of blocks */ BEST_TRY_CONTIGUOUS yes /* as close as possible */ BUCKET_SIZE 2 /* number of blocks in record */ EXTENSION 2 /* blocks for extension */ AREA 1 ALLOCATION 3 BEST_TRY_CONTIGUOUS yes BUCKET_SIZE 2 EXTENSION 0 AREA 2 . . . AREA 7 ALLOCATION 2 BEST_TRY_CONTIGUOUS yes BUCKET_SIZE 1 EXTENSION 0 KEY 0 /* primary key */ CHANGES no /* cannot be changed */ DATA_AREA 0 /* data in AREA 0 */ DATA_FILL 100 /* %fill for data */ DUPLICATES no /* not allowed */ INDEX_AREA 1 /* index in AREA 1 */ INDEX_FILL 100 /* %fill for index */ LEVEL1_INDEX_AREA 1 /* level-1 index in AREA 1 */ PROLOGUE 2 /* type for index */ SEG0_LENGTH 20 /* key length */ SEG0_POSITION 0 /* key position */ TYPE string /* character string */ . . . KEY 3 /* third alternate key */ CHANGES yes DATA_AREA 6 DATA_FILL 100 DUPLICATES yes INDEX_AREA 7 INDEX_FILL 100 LEVEL1_INDEX_AREA 7 SEG0_LENGTH 3 SEG1_LENGTH 3 SEG2_LENGTH 3 SEG0_POSITION 0 SEG1_POSITION 20 SEG2_POSITION 40 TYPE string |
After creating an FDL file with EDIT/FDL or a text editor, you can then
use the command CREATE/FDL, which uses the specifications in the FDL
file to create a new, empty data file. Thus, you can use EDIT/FDL to
define the data file, and then create the data file when it is required
later. See the OpenVMS File Definition Language Facility Reference Manual for further details.
6.7.2.2 Using a PL/I Program
You can write a PL/I program to define an indexed sequential file or to create an indexed sequential file from an existing definition, or both. The following lines in the program enable the optional generation of a file definition and the use of that definition to create a file:
%INCLUDE FDL$CREATE; . . . CALL FDL$CREATE ('STATE50.FDL'); |
The following program, CREATE50, defines a file similar to the file defined with the FDL Facility in Section 6.7.2.1.
CREATE50: PROCEDURE OPTIONS(MAIN); DECLARE ANSWER CHAR(1); /* answer variable */ DECLARE (I,J) FIXED BIN(31); /*index control variables */ %INCLUDE FDL$CREATE; /* default allocation, bucket size, and extent for each area */ DECLARE AREA_ATTRIBUTES(0:7,0:2) FIXED BIN(7) STATIC INITIAL ( 25, 2, 2, 2, 2, 0, 15, 2, 1, 3, 2, 0, 20, 2, 2, 3, 2, 0, 9, 1, 0, 7, 1, 0); /* default areas, fills, segment lengths, and positions for each key */ DECLARE KEY_ATTRIBUTES(0:3,0:10) FIXED BIN(7) STATIC INITIAL ( 0, 100, 1, 100, 1, 20, 0, 0, 0, 0, 0, 2, 100, 3, 100, 3, 20, 0, 0, 20, 0, 0, 4, 100, 5, 100, 5, 20, 0, 0, 40, 0, 0, 6, 100, 7, 100, 7, 3, 3, 3, 0, 20, 40); /* FDL file that will be created if necessary */ DECLARE FDL FILE PRINT OUTPUT; /* find out if FDL file already exists */ GET LIST (ANSWER) OPTIONS(PROMPT('Have you already created STATE50.FDL with EDIT/FDL or EDT? ')); /* file definition (.FDL) file creation */ IF ANSWER = 'N' | ANSWER = 'n' THEN DO; PUT SKIP LIST (' Creating STATE50.FDL.....'); OPEN FILE (FDL) TITLE ('state50.fdl') PAGESIZE(32767); /* define system */ PUT SKIP FILE (FDL) LIST ('SYSTEM'); PUT SKIP FILE (FDL) LIST (' SOURCE VAX/VMS'); /* define file */ PUT SKIP FILE (FDL) LIST ('FILE'); PUT SKIP FILE (FDL) LIST (' NAME state50.dat'); PUT SKIP FILE (FDL) LIST (' ORGANIZATION indexed'); /* define record */ PUT SKIP FILE (FDL) LIST ('RECORD'); PUT SKIP FILE (FDL) LIST (' CARRIAGE_CONTROL carriage_return'); PUT SKIP FILE (FDL) LIST (' FORMAT fixed'); PUT SKIP FILE (FDL) LIST (' SIZE 60'); /* define areas */ DO I = 0 TO HBOUND (AREA_ATTRIBUTES,1); PUT SKIP FILE (FDL) LIST ('AREA '||trim(character(i))); PUT SKIP FILE (FDL) LIST (' ALLOCATION '|| character(area_attributes(i,0))); PUT SKIP FILE (FDL) LIST (' BEST_TRY_CONTIGUOUS yes'); PUT SKIP FILE (FDL) LIST (' BUCKET_SIZE '|| character(area_attributes(i,1))); PUT SKIP FILE (FDL) LIST (' EXTENSION '|| character(area_attributes(i,2))); END; /* define keys */ DO I = 0 TO HBOUND (KEY_ATTRIBUTES,1); PUT SKIP FILE (FDL) LIST ('KEY '||TRIM(CHARACTER(I))); IF I = 0 THEN PUT SKIP FILE (FDL) LIST (' CHANGES no'); ELSE PUT SKIP FILE (FDL) LIST (' CHANGES yes'); PUT SKIP FILE (FDL) LIST (' DATA_AREA '|| CHARACTER(KEY_ATTRIBUTES(I,0))); PUT SKIP FILE (FDL) LIST (' DATA_FILL '|| CHARACTER(KEY_ATTRIBUTES(I,1))); IF I = 0 THEN PUT SKIP FILE (FDL) LIST (' DUPLICATES no'); ELSE PUT SKIP FILE (FDL) LIST (' DUPLICATES yes'); PUT SKIP FILE (FDL) LIST (' INDEX_AREA '|| CHARACTER(KEY_ATTRIBUTES(I,2))); PUT SKIP FILE (FDL) LIST (' INDEX_FILL '|| CHARACTER(KEY_ATTRIBUTES(I,3))); PUT SKIP FILE (FDL) LIST (' LEVEL1_INDEX_AREA '|| CHARACTER(KEY_ATTRIBUTES(I,4))); DO J = 0 TO 2; IF KEY_ATTRIBUTES(I,5+J*2) ^= 0 THEN DO; PUT SKIP FILE (FDL) LIST (' SEG'||TRIM(CHARACTER(J))||'_LENGTH '|| CHARACTER(KEY_ATTRIBUTES(I,5+J*2))); PUT SKIP FILE (FDL) LIST (' SEG'||trim(character(j))||'_POSITION '|| CHARACTER(KEY_ATTRIBUTES(I,5+J*2))); END; END; PUT SKIP FILE (FDL) LIST (' TYPE string'); END; CLOSE FILE (FDL); END; /* create STATE50.DAT using STATE50.FDL as the definition file */ PUT SKIP LIST (' Creating file using STATE50.FDL.....'); CALL FDL$CREATE ('STATE50.FDL'); END CREATE50; |
This program uses the same FDL statements that would be used if you had issued the EDIT/FDL command. Notice that the program asks you whether you have already created the file and, if so, calls FDL$CREATE, which permits you to use a preexisting file definition.
Note that the following command:
$ CREATE/FDL=STATE50 |
is equivalent to the following command and response:
$ RUN CREATE50 Have you already created STATE50.FDL with EDIT/FDL or EDT? Y |
Previous | Next | Contents | Index |