Kednos PL/I for OpenVMS Systems
User Manual


Previous Contents Index

6.7.3 Defining Keys

An indexed sequential file must have at least one key. The first (primary) key is always numbered 0. An indexed sequential file can have up to 255 keys; however, for file-processing efficiency it is recommended that you define no more than 7 or 8 keys. (The time required to insert a new record or update an existing record is directly related to the number of keys defined; the retrieval time for an existing record, however, is unaffected by the number of keys.)

When you design an indexed sequential file, you must define each key in the following terms:

When you want to define more than one key, or to define keys of different data types, you must be careful when you specify the key fields. The next few subsections describe some considerations for specifying keys.

Specifying Key Position and Size

When you specify a key, you must specify both its position in the record and its length. The position is specified with respect to the beginning of the record; thus, a key that is positioned beginning in the first byte of the record has a starting position of 0, a key positioned beginning in the 21st byte has a key position of 20, and so on.

If the ENVIRONMENT option SCALARVARYING is in effect, the key size for a CHARACTER VARYING key should be 2 more than your declared maximum size; you specify a key position offset 2 from the variable base offset.

To determine the key positions for fields within a structure, you can examine the storage map in the program listing that defines the structure. The following structure definition illustrates the relationships between key field definitions and the storage map offsets:


    1      FOO: PROCEDURE; 
    2    1 DECLARE 1 STATE BASED (STATE_PTR), 
    3    1             2 NAME CHARACTER (20), /*Primary key */ (1)
    4    1             2 POPULATION FIXED BINARY(31), /*3rd alternate key */(4)
    5    1             2 CAPITAL, 
    6    1                 3 NAME CHARACTER(20), 
    7    1                 3 POPULATION FIXED BINARY(31), 
    8    1             2 LARGEST_CITIES(2), 
    9    1                 3 NAME CHARACTER(30), 
   10    1                 3 POPULATION FIXED BINARY(31), 
   11    1             2 SYMBOLS, 
   12    1                 3 FLOWER CHARACTER(30), /*1st alternate key*/ (2)
   13    1                 3 BIRD CHARACTER(30); /*2nd alternate key*/ (3)
   14    1 
   15    1 END FOO; 

The resultant storage map shows the following:

  1. The primary key is 20 bytes, offset 0 bytes from the base of the structure or record.
  2. The first alternate key is 30 bytes, offset 116 bytes from the base of the structure or record.
  3. The second alternate key is 30 bytes, offset 146 bytes from the base of the structure or record.
  4. The third alternate key is four bytes, offset 20 bytes from the base of the structure or record.

The storage map is:


           +-------------+ 
           | Storage Map | 
           +-------------+ 
 
 
External Entry Points and Variables Declared Outside Procedures 
--------------------------------------------------------------- 
 
 Identifier Name                 Storage     Size     Line  Attributes 
 ---------- ----                 -------     ----     ----  ---------- 
 
 
 FOO                                                  1     ENTRY, EXTERNAL 
 
 
Procedure FOO on line 1 
----------------------- 
 
 Identifier Name                 Storage     Size     Line  Attributes 
 ---------- ----                 -------     ----     ----  ---------- 
 
 
 BIRD                                        30 BY    13    OFFSET FROM BASE IS 146 BY, 
                                                            MEMBER OF STRUCTURE SYMBOLS, 
                                                            CHARACTER(30) UNALIGNED, NONVARYING (3)
 
 CAPITAL                                     24 BY    5     OFFSET FROM BASE IS 24 BY, 
                                                            MEMBER OF STRUCTURE STATE, STRUCTURE 
 
 FLOWER                                      30 BY    12    OFFSET FROM BASE IS 116 BY, 
                                                            MEMBER OF STRUCTURE SYMBOLS, 
                                                            CHARACTER(30) UNALIGNED, NONVARYING (2)
 
 LARGEST_CITIES                              68 BY    8     OFFSET FROM BASE IS 48 BY, 
                                                            MEMBER OF STRUCTURE STATE, STRUCTURE 
                                                            DIMENSION 
 
 NAME                                        30 BY    9     OFFSET FROM BASE IS 48 BY, MEMBER OF 
                                                            STRUCTURE LARGEST_CITIES 
                                                            CHARACTER(30), UNALIGNED, NONVARYING 
 
 NAME                                        20 BY    6     OFFSET FROM BASE IS 24 BY, MEMBER OF 
                                                            STRUCTURE CAPITAL, CHARACTER(20) 
                                                            UNALIGNED, NONVARYING 
 
 NAME                                        20 BY    3     OFFSET FROM BASE IS 0 BY, MEMBER OF 
                                                            STRUCTURE STATE, CHARACTER(20) 
                                                            UNALIGNED, NONVARYING (1)
 
 POPULATION                                  4 BY     10    OFFSET FROM BASE IS 78 BY, MEMBER OF 
                                                            STRUCTURE LARGEST_CITIES 
                                                            FIXED BIN(31,0), ALIGNED, PRECISION 
 
 POPULATION                                  4 BY     7     OFFSET FROM BASE IS 44 BY, MEMBER OF 
                                                            STRUCTURE CAPITAL, FIXED BIN(31,0) 
                                                            ALIGNED, PRECISION 
 
 POPULATION                                  4 BY     4     OFFSET FROM BASE IS 20 BY, MEMBER OF 
                                                            STRUCTURE STATE, FIXED BIN(31,0) 
                                                            ALIGNED, PRECISION 
(4)
 
 STATE                           based       176 BY   2     STRUCTURE 
 
 SYMBOLS                                     60 BY    11    OFFSET FROM BASE IS 116 BY, MEMBER OF 
                                                            STRUCTURE STATE, STRUCTURE 
 
   .
   .
   .
 

After you enter the EDIT/FDL command, you can specify the keys to FDL as follows:


        Enter Desired Primary        (Keyword)[-]   : KEY 0
 
                - Legal KEY 0 Secondary Attributes - 
 
CHANGES                 yes/no    LEVEL1_INDEX_AREA   number 
DATA_AREA               number    NAME                string 
DATA_FILL               number    NULL_KEY            yes/no 
DATA_KEY_COMPRESSION    yes/no    NULL_VALUE          char/num 
DATA_RECORD_COMPRESSION yes/no    POSITION            number 
DUPLICATES              yes/no    PROLOGUE            number 
INDEX_AREA              number    TYPE                keyword 
INDEX_COMPRESSION       yes/no    SEGn_LENGTH         number 
INDEX_FILL              number    SEGn_POSITION       number 
LENGTH                  number
 
   Enter KEY 0 Attribute       (Keyword)[-]   :POSITION[Return]
 
 
KEY 0 
        SEG0_POSITION
 
Enter value for this Secondary  (0-16299)[-]   :0[Return]
 
                        - Resulting Primary Section - 
 
KEY 0 
        SEG0_POSITION          0
 
Continue with this Same Primary (Yes/No)[No]   :YES[Return]

At this point, the menu Secondary Attributes reappears on your screen:


Enter KEY 0 Attribute        (Keyword)[-]   :LENGTH[Return]
 
KEY 0 
         SEG0_LENGTH 
Enter value for this Secondary  (0-255)[-]  :20[Return]
 
 
                        - Resulting Primary Section - 
 
KEY 0 
         SEG0_LENGTH           20 
         SEG0_POSITION         0 
 
Continue with this Same Primary (Yes/No)[No]  :[Return]

At this point, you return to the first menu. To establish the keys necessary for ADDRESS.DAT, you need to step through the entire process again. In response to the prompt after the first menu, type ADD; in response to the prompt after the second menu, type KEY 1. Give KEY 1 (for example) a position of 116 and a length of 30.

Establish the third and last key the same way. Give KEY 2 a position of 146 and a length of 30. Then go back to the first menu and type VIEW in response to the prompt. A summary appears as follows:


KEY 0 
        SEG0_LENGTH           20 
        SEG0_POSITION         0 
 
 
KEY 1 
        SEG0_LENGTH           30 
        SEG0_POSITION         116 
 
 
KEY 2 
        SEG0_LENGTH           30 
        SEG0_POSITION         146 

Use FDL to change information if necessary.

Key Data Types

Table 6-2 summarizes the valid data types for keys in RMS indexed sequential files, lists the corresponding PL/I data type declaration, and shows how to specify the key data type and length to the FDL Facility.

Table 6-2 Key Data Types

Data Type

PL/I Declaration
FDL
Specification
String 1 CHAR(n), where
1 < n < 255
STR
n
15-bit signed
integer
FIXED BINARY(15) INT
2
31-bit signed
integer
FIXED BINARY(31) INT
4
63-bit signed
integer
FIXED BINARY(63) INT
8
16-bit unsigned
binary 2
FIXED BINARY(15) BIN
2
32-bit unsigned
binary 2
FIXED BINARY(31) BIN
4
64-bit unsigned
binary 2
FIXED BINARY(64) BIN
8
Packed decimal FIXED DECIMAL(n)
where 1 < n < 16
DECIMAL
n


1PL/I supports segmented character-string keys.
2PL/I does not distinguish between signed and unsigned integers. Thus, the difference between signed integer keys and unsigned binary keys is in the key collating sequence. For signed integer keys, the collating sequence is from the lowest negative number to the highest positive number (for example -32768, -32767, ... 0, 1, 2, ... 32767). For unsigned binary keys, the collating sequence is from zero to the highest positive number, then from the lowest negative number to -1 (for example 0, 1, 2, ... 32766, 32767, -32768, -32767, ... -1).

Index Numbers

When you define the keys in an indexed sequential file, you must assign an index number to each alternate key. The index number of the primary key is always 0; subsequent alternate key indexes are numbered 1, 2, and so on. When you create a file with the FDL Facility, you must define the keys in index number order.

When you want to access a record in an indexed sequential file by an alternate key, you specify the index number. You can specify the index number either in the INDEX_NUMBER option of ENVIRONMENT or in the INDEX_NUMBER option of a record I/O statement.

For example, to access the record for a state whose flower is MAGNOLIA in the indexed file STATE_FILE, when the current index is not 1, you must specify the index number 1, as in the following example:


READ FILE(STATE_FILE) 
INTO(STATE) KEY('MAGNOLIA') 
         OPTIONS(INDEX_NUMBER(1)); 
This READ statement reads the first record whose key in the first alternate index is MAGNOLIA and sets the current index number to 1.

Key Options

When you define alternate indexes for an indexed sequential file, you can specify the following information:

These options are described in the OpenVMS File Definition Language Facility Reference Manual.

6.7.4 Using Indexed Sequential Files

After you have defined and created an indexed sequential file, you can write records to it by opening it with the UPDATE attribute and using PL/I WRITE statements. For example:


OPEN FILE(STATE_FILE) RECORD DIRECT UPDATE; 
   .
   .
   .
WRITE FILE(STATE_FILE) FROM(STATE) KEYFROM(STATE.NAME); 

This WRITE statement writes the record whose key value is specified by the field STATE.NAME in the structure STATE.

When a WRITE statement adds a record to an indexed sequential file, the value of the KEYFROM option must always be the primary key. In fact, the WRITE statement causes the index number to be reset to zero if any other index number is in effect.

Note

When PL/I copies the KEYFROM value into the record, it overwrites anything already in those positions, while distributing segmented values as specified by the RMS key description. Therefore, it is important that the key value come from some variable other than the record variable itself.

Populating an Indexed Sequential File

There are two techniques for optimizing the initial population (loading) of an indexed sequential file:

For details on specifying fill numbers for the FDL Facility, see the OpenVMS File Definition Language Facility Reference Manual.

Reading an Indexed Sequential File Sequentially

To read records in an indexed sequential file in collating order by key value, open the file with the INPUT and SEQUENTIAL attributes. The following example illustrates reading the file STATE_FILE in sequential order using the primary key, that is, using the STATE.NAME field. This procedure uses the SET option of the READ statement; thus, no space is required in the records.


DECLARE STATE_PTR POINTER, 
        STATE_FILE FILE, 
        EOF BIT(1) INITIAL ('0'B); 
DECLARE 1 STATE BASED (STATE_PTR), 
          2 NAME CHARACTER(20), 
          .
          .
          .
ON ENDFILE(STATE_FILE) EOF = '1'B; 
OPEN FILE(STATE_FILE) INPUT SEQUENTIAL; 
READ FILE(STATE_FILE) SET(STATE_PTR); 
DO WHILE (^EOF); 
    PUT SKIP(2) LIST('State:',STATE.NAME); 
    PUT SKIP(2) EDIT('Population:',STATE.POPULATION) 
                        (A,P'ZZ,ZZZ,ZZZ'); 
      .
      .
      .
    READ FILE(STATE_FILE) SET(STATE_PTR); 
    END; 
In this example, the procedure reads the records in the file STATE_FILE beginning with the first record in its primary index, the NAME field.

Accessing Records by Alternate Key

To use an alternate key to read a record in an indexed sequential file, specify the INDEX_NUMBER option on the READ statement.

For example, if a file containing data about states has as its primary key the state name, it might have alternate keys for state flowers, birds, and so on. Assuming that a field called FLOWER is the first alternate key, you could access the record for a state whose flower is MAGNOLIA by writing the following statements:


OPEN FILE(STATE_FILE) KEYED INPUT; 
READ FILE(STATE_FILE) SET(STATE_PTR) KEY('MAGNOLIA') 
      OPTIONS(INDEX_NUMBER(1)); 

The INDEX_NUMBER option followed by 1 specifies the first alternate index, the FLOWER field. The INDEX_NUMBER option is also valid on the REWRITE and DELETE statements.

You can access a file starting with an alternate index by opening the file with the INDEX_NUMBER option of the ENVIRONMENT attribute as in the following example:


OPEN FILE(STATE_FILE) SEQUENTIAL INPUT ENV( 
              INDEX_NUMBER(2)); 
READ FILE(STATE_FILE) SET(STATE_PTR); 
DO WHILE (^EOF); 
    PUT SKIP EDIT(STATE.BIRD,'is the bird of',STATE.NAME) 
                 (A,X,A,X,A); 
    READ FILE(STATE_FILE) SET(STATE_PTR); 
    END; 
These statements, executed until the end-of-file is reached, access the records in the file STATE_FILE on the basis of its second alternate index, the BIRD field.

Updating Records in an Indexed Sequential File

You can modify records in an indexed sequential file by opening the file with the UPDATE attribute, and using the REWRITE and DELETE statements to modify or delete records from the file.

The following example shows the correction of an invalid field in a record in the file STATE_FILE:


DECLARE (STATENAME,NEWFLOWER) CHARACTER(30) VARYING; 
       .
       .
       .
    OPEN FILE(STATE_FILE) KEYED SEQUENTIAL UPDATE; 
    GET SKIP LIST(STATENAME) 
                 OPTIONS (PROMPT('State: ')); 
    READ FILE(STATE_FILE) SET(STATE_PTR) 
             KEY(STATENAME); 
    GET SKIP LIST(NEWFLOWER) OPTIONS( 
                 PROMPT('New state flower name: ')); 
    STATE.FLOWER = NEWFLOWER; 
    REWRITE FILE(STATE_FILE); 
The REWRITE statement rewrites the current record in the file, that is, the record that was just read with the READ SET statement.

Specifying the Type of Key Match

RMS allows generic key matching; that is, it can locate a record in an indexed sequential file whose key is greater than a specified key value, or whose key is greater than or equal to a specified key value. In PL/I, you can access records by generic key using the MATCH_NEXT and MATCH_NEXT_EQUAL options on a record I/O statement.

For example, the file STATE_FILE's third alternate key is the state's population. To list all states whose populations are greater than a specified size, you could open and read the file as follows:


DECLARE EOF BIT(1) STATIC INIT('0'B), 
        SIZE FIXED BIN(31); 
 
        OPEN FILE(STATE_FILE) KEYED SEQUENTIAL ENV( 
                INDEX_NUMBER(3)); 
        GET LIST(SIZE) OPTIONS(PROMPT('Starting population: ')); 
        READ FILE(STATE_FILE) SET (STATE_PTR) KEY(SIZE) 
                        OPTIONS(MATCH_NEXT_EQUAL); 
        DO WHILE (^EOF); 
            PUT SKIP EDIT(STATE.NAME,'Population is ', 
                                STATE.POPULATION) 
                        (A,A,P'ZZ,ZZZ,ZZZ'); 
            READ FILE(STATE_FILE) SET (STATE_PTR); 
            END; 
In this example, the size is obtained by a GET statement. The procedure opens the file, specifying the third alternate index, the POPULATION field, in the INDEX_NUMBER option. After accessing a record by matching the first key in this index that equals or exceeds the size entered, the procedure reads the file sequentially to the end-of-file, using that index.

Segmented Keys

PL/I supports the RMS segmented key concept. Segmented keys are used when a single key cannot be made in contiguous fields of the record. The key's parts are split up over the record, or reordered within a contiguous part of the record. Segmenting the key has the advantage of using only one key to represent several fields that are always defined and sorted as a conceptual unit.

When there is a KEYFROM clause with a segmented key, PL/I takes its usual action of copying the key value into the record variable and then writing the variable directly to disk through RMS.

PL/I copies the one contiguous value to the position or positions specified by the RMS definition for the segmented key. This means it must break up the value into subfields or segments. Those fields are determined by the RMS segment sizes for the key. Thus, for a 4-segment key of 8 bytes and individual sizes of 1, 1, 4, and 2, respectively, it would break up the one contiguous key value ABCDEFGH as follows:

Then it copies each segment into its correct position in the record-the positions as specified by RMS, not your record variable structure declaration.

When RMS orders the records by the key, it reconstructs the original key value as one piece and sorts it that way. Thus the relative ordering of the segments within the record has no bearing on how the original value is sorted.

These actions are the same for nonsegmented keys in PL/I. They are treated as keys of one segment only, of the appropriate RMS-determined data type.

If you need to use segmented keys, be aware of the following considerations and constraints:

Error Handling

PL/I signals the KEY condition when errors occur while keys for indexed sequential files are being processed. For example, if a key value specified on a READ statement specifies a key that does not exist, or if a WRITE statement attempts to write a record using a primary key value that already exists, the KEY condition is signaled.

You can write an ON-unit to detect and correct some of the more common key errors. For an example of an ON-unit that detects whether a record with a given key value was not found or whether an attempt was made to write a record whose key duplicates the value of an existing key, see Chapter 4.


Previous Next Contents Index