[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here

HP BASIC for OpenVMS
User Manual


Previous Contents Index

13.6.4 Reading Records

The GET statement moves a record from a file to a record buffer and makes the data available for processing. GET statements are valid on sequential, relative, and indexed files. You should not use GET statements on terminal-format files or virtual array files.

For sequential files, a sequential GET retrieves the next record in the file. For relative files, a sequential GET retrieves the next existing record. For indexed files, a sequential GET retrieves the record with the next ascending or descending value in the current key of reference, depending on that key's collating sequence.

Table 13-2 shows the current record and next record pointers after a GET operation. Note that the values of these pointers vary, depending on whether or not the previous operation was a FIND.

Table 13-2 Record Context After a GET Operation
Record Access
Mode
File
Type
Current
Record
Next Record
Sequential GET
with FIND
Sequential Record found Current record + 1
  Relative Record found Next existing record
  Indexed Record found Next record in current key
Sequential GET
without FIND
Sequential Next record Next record + 1
  Relative Next existing record Next existing record + 1
  Indexed Next record in current key Record following next
record in current key
Random GET All Record specified Next record in succession

If you precede a sequential GET operation with a FIND operation, the current record is the one located by FIND. If you do not perform a FIND operation before a sequential GET operation, the current record is the next sequential record.

The following example shows the use of the GET operation to sequentially access records in an indexed file. The example opens an indexed file and displays the first 25 records with serial numbers greater than AB2721 in ascending primary key value order.


MAP (Bec) STRING Owner = 30%, LONG Vehicle_number,     &
          STRING Serial_number = 22%
OPEN "VEH.IDN" FOR INPUT AS FILE #2%,                  &
      ORGANIZATION INDEXED, PRIMARY KEY Serial_number, &
      MAP Bec, ACCESS READ
GET #2%, KEY #0% EQ "AB2721"
FOR I% = 1% TO 25%
      GET #2%
      PRINT "Vehicle Number = ";Vehicle_number
      PRINT "Owner is: ";Owner
      PRINT
NEXT I%

The following example performs random GET operations by specifying a record number:


MAP (Bec) STRING Owner = 30%, LONG Vehicle_number,    &
          STRING Serial_number = 22%
OPEN "VEH.IDN" FOR INPUT AS FILE #2%,                 &
      ORGANIZATION SEQUENTIAL FIXED,                  &
      MAP Bec, ACCESS READ
INPUT "Which record do you want";A%
WHILE (A% <> 0%)
   GET #2%, RECORD A%
   PRINT "The vehicle number is", Vehicle_number
   PRINT "The serial number is", Serial_number
   PRINT "The owner of vehicle";Vehicle_number; "is", Owner
   INPUT "Next Record";A%
NEXT
CLOSE #2%
END

You can specify an ALLOW clause in a GET statement if you have opened the file with ACCESS MODIFY or ACCESS WRITE and UNLOCK EXPLICIT. The ALLOW clause lets you control the type of lock RMS places on the retrieved record. ALLOW NONE specifies that no other users can access this record (this is the default). ALLOW READ lets other access streams have read access to the record. That is, other users can retrieve the record, but cannot perform DELETE, PUT, or UPDATE operations on it. ALLOW MODIFY lets other access streams perform GET, DELETE, or UPDATE operations on the record.

If you are trying to access a locked record, BASIC signals "Record/bucket locked" (ERR=154). However, if you only need to read this record, you can override the lock with the REGARDLESS clause. The REGARDLESS clause allows you to read a locked record. Use caution when using the REGARDLESS clause because a record accessed in this way might be in the process of being changed by another program.

Alternatively, you can also specify the WAIT clause on a GET statement; the WAIT clause allows you to handle record locked conditions by waiting for the record to become available. Note that if a WAIT clause is specified on a GET operation to a unit-record device such as a terminal, the integer expression indicates how long to wait for the I/O to complete, rather than how long to wait on a record locked condition. For more information, see Section 13.6.9.

13.6.5 Writing Records

For a file opened with ACCESS WRITE or ACCESS MODIFY, the PUT statement moves data from the record buffer to a file using the I/O buffer. PUT statements are valid on RMS sequential, relative, and indexed files. You cannot use PUT statements on terminal-format files or virtual array files.

Sequential access is valid on RMS sequential, relative, and indexed files. For sequential, variable, and stream files, a sequential PUT operation adds a record at the end of the file. For sequential fixed and relative files, PUT writes records sequentially or randomly depending on the presence of a RECORD clause. For indexed files, RMS stores records in order of the primary key's collating sequence; therefore, you do not need to specify a random or sequential PUT. Table 13-3 shows the record context after both random and sequential PUT operations.

Table 13-3 Record Context After a PUT Operation
Record Access
Mode
File
Type
Current
Record
Next Record
Sequential PUT Sequential None End of file
Sequential PUT Relative None Next record
Sequential PUT Indexed None Undefined
Random PUT Relative None Unchanged

After a PUT operation, the current record pointer has no value. However, the value of the next record pointer changes depending on the file type and the record access mode used with the PUT operation. In a sequential, stream, or variable file, records can only be added at the end of the file; therefore, the next record after PUT is the end of the file. In a relative, sequential, or fixed file, the next record after a PUT operation is the next logical record.

The following example opens a sequential file with ACCESS APPEND specified. For sequential files, this is almost identical to ACCESS WRITE. The only difference is that, with ACCESS APPEND, BASIC positions the file pointer after the last record in the file when it opens the file for processing. All subsequent PUT operations append the new record to the end of the existing file.


MAP (Buff) STRING Code = 4%, Exp_date = 9%, Type_desig = 32%
OPEN "INV.DAT"FOR INPUT AS FILE #2%,                  &
      ORGANIZATION SEQUENTIAL FIXED, ACCESS APPEND,   &
      MAP Buff
WHILE -1%
      INPUT "What is the specification code";Code
      INPUT "What is the expiration date";Exp_date
      INPUT "What is the designator";Type_desig
      PUT #2%
NEXT

If the current record pointer is not at the end of the file when you attempt a sequential PUT operation to a sequential file, BASIC signals "Not at end of file" (ERR=149).

In the following example, the PUT statement writes records to an indexed file. In this case, the error message "Duplicate key detected" (ERR=134) indicates that a record with a matching key field already exists, and you did not allow duplicates on that key.


10      MAP (Purchase_rec) STRING R_num = 5,              &
                                  Dept_name = 10,         &
                                  Pur_dat = 9
20      OPEN "INFO.DAT"FOR OUTPUT AS FILE #2,               &
                ORGANIZATION INDEXED FIXED, ACCESS WRITE, &
                PRIMARY KEY R_num, MAP Purchase_rec
30      WHILE -1%
                INPUT "Requisition number";R_num
                INPUT "Department name";Dept_name
                INPUT "Date of purchase";Pur_dat
                PRINT
                PUT #2%
        NEXT


Requisition number? 2522A
Department name? COSMETICS
Date of purchase? 15-JUNE-1985

Requisition number? 2678D
Department name? AUTOMOTIVE
Date of purchase? 15-JUNE-1985

Requisition number? 4167C
Department name? AUTOMOTIVE
Date of purchase? 6-JANUARY-1985

Requisition number? 2522A
Department name? SPORTING GOODS
Date of purchase? 25-FEBRUARY-1985

%BAS-F-DUPKEYDET, Duplicate key detected
-BAS-I-ON_CHAFIL, on channel 2 for file USER$$DISK:[MAGNUS]INFO.DAT;8 at
user PC 0017E593
-BAS-O-FROLINMOD, from line 30 in module DUPLICATES
-RMS-F-DUP, duplicate key detected (DUP not set)

13.6.6 Deleting Records

The DELETE statement removes a record from a file that was opened with ACCESS MODIFY. After you have deleted a record you cannot retrieve it. DELETE works with relative and indexed files only.

A successful FIND or GET operation must precede the DELETE operation. These operations make the target record available for deletion. In the following example, the FIND statement locates record 67 in a relative file and the DELETE statement removes this record from the file. Because the cell itself is not deleted, you can use the PUT statement to write a record into that cell after deleting its contents.


FIND #1%, RECORD 67%
DELETE #1%

Note

There is no current record after a deletion. The next record pointer is unchanged.

13.6.7 Updating Records

The UPDATE statement writes a new record at the location indicated by the current record pointer. UPDATE is valid on RMS sequential, relative, and indexed files.

UPDATE operates on the current record, provided that you have write access to that record. In order to successfully update a variable-length record, you must know the exact size of the record you want to update. BASIC has access to this information after a successful GET operation. If you have not performed a successful GET operation on the variable-length record, then you must specify a COUNT clause in the UPDATE statement that contains the record size information.

If you are updating a variable length record, and the record that you want to write out is of different size than the record you retrieved, you must use a COUNT clause.

An UPDATE will fail with the exception "No current record" (ERR=131) if you have not previously established a current record with a successful GET or FIND. Therefore, when updating records you should include error trapping in your program to make sure all GET operations execute successfully.

An UPDATE operation on a sequential file is valid only when:

  • The file containing the record is on disk.
  • The new record is the same size as the one it is replacing.
  • You have established a current record through a GET or FIND operation. Note that COUNT defaults to the size of the current record if a GET was performed. If a FIND operation was used to locate the current record, then you must supply a COUNT value.

The following program searches to find a record in which the L_name field matches the specified Search_name$. Once this record is found and retrieved, the Rm_num field of that record is updated; the program then prompts for another Search_name$. If a match is not found, BASIC prints the message "No such record" and prompts the user for another Search_name$. The program ends when the user enters a null string for the Search_name$ value.


20      MAP (AAA) STRING L_name = 60%, F_name = 20%, Rm_num = 8%
30      OPEN "STU.DAT"FOR INPUT AS FILE #9%,           &
                ORGANIZATION SEQUENTIAL FIXED, MAP AAA
50      INPUT "Last name";Search_name$
55      Search_name$ = EDIT$(Search_name$, -1%)
60      IF Search_name$ = ""
                THEN GOTO 32010
        END IF
65      RESTORE #9%
70      WHEN ERROR IN
75         GET #9% WHILE Search_name$ <> L_name
        USE
           IF ERR=11
               THEN
                   PRINT "No such record"
                   CONTINUE 50
               ELSE
                   EXIT HANDLER
           END IF
        END WHEN
80      INPUT "Room number";Rm_num
90      UPDATE #9%
100     GOTO 50
32010   CLOSE #9%
32030   PRINT "Update complete"
32767   END

Note

An UPDATE operation invalidates the value of the current record pointer. The next record pointer is unchanged.

When you update a record in a relative variable file, the new record can be larger or smaller than the record it replaces, provided that it is smaller than the maximum record size set for the file. When you update a record in an indexed variable file, the new record can also be larger or smaller than the record it replaces. The updated record:

  • Can be no longer than the maximum record size, if specified
  • Must include at least the primary key field

The following program updates a specified record on an indexed file:


MAP (UPD) STRING Enrdat = 8%, LONG Part_num, Sh_num, REAL Cost
OPEN "REC.ING"FOR INPUT AS FILE #8%,                    &
      INDEXED, MAP UPD, PRIMARY KEY Part_num
INPUT "Part number to update";A%
Loop1:
WHILE -1%
   GET #8%, KEY #0%, EQ A%
   INPUT "Revised Cost is";Cost
   UPDATE #8%
   INPUT "Next Record";A%
   IF A% = 0%
   THEN
        EXIT Loop1
   END IF
NEXT
CLOSE #8%
END

If the new record either omits one of the old record's alternate key fields or changes one of them, the OPEN statement must specify a CHANGES clause for that key field when the file is created. Otherwise, BASIC signals the error "Key not changeable" (ERR=130).

13.6.8 Controlling Record Access

When you open a file, BASIC allows you to specify how you will access the file and what types of access you will allow other running programs while you have the file open.

If you open a file for read access only (ACCESS READ), BASIC by default allows other programs to have unrestricted access to the file. You can restrict access with an ALLOW clause only if the file's security constraints allow you write access to the file.

BASIC by default prevents access by other programs to any file you open with ACCESS WRITE, ACCESS MODIFY, or ACCESS SCRATCH (sequential files only). This default action is equivalent to specifying the OPEN statement ALLOW NONE clause. To allow less restrictive access to the open file, specify ALLOW READ or ALLOW MODIFY.

When a file is open for read access only and you have not restricted access to other programs with ALLOW NONE, BASIC allows other programs to read any record in the file including records that your program is concurrently accessing. However, when you retrieve a record with the GET statement from a file you have opened with the intent to modify, BASIC normally restricts other programs from accessing that record. This restriction is called locking.

To allow other programs to access a record you have locked, you must release the lock on the record in one of the following ways:

  • Retrieve another record on the same channel. Unless you have opened the file with the UNLOCK EXPLICIT clause (see the following discussion), this action will unlock the previous record.
  • Explicitly unlock the record with the UNLOCK or FREE statement. The UNLOCK statement releases the current record. The FREE statement releases all records locked on a given channel.
  • Perform an UPDATE operation on the record. An UPDATE statement causes the current record to be unlocked.
  • Close the file.

In addition to the capability of restricting access through the OPEN statement ALLOW clause, BASIC allows programs to explicitly control record locking on each record that is retrieved. To use explicit record locking on a file, the OPEN statement must include an UNLOCK EXPLICIT clause. You may then optionally specify an ALLOW clause on the GET and FIND statements. The ALLOW clause on a GET or FIND statement specifies the type of access allowed by other programs to the record while you are accessing it. The following statement specifies that other programs may read but not modify the records you have locked:


GET #1, ALLOW READ

If you specify UNLOCK EXPLICIT when opening a file, all records that you retrieve remain locked until you explicitly unlock them with a FREE, UNLOCK, or CLOSE statement.

13.6.9 Gaining Access to Locked Records

If you are trying to access a record that is currently locked, one possible solution is to use the REGARDLESS clause on the GET or FIND statement. The REGARDLESS clause is useful when you are interested in having only read access to the specified record. Be aware, however, that using the REGARDLESS clause to read a locked record can lead to unexpected results because the record you read can be in the process of being changed by another program.

Another solution is to include a WAIT clause on the GET or FIND statement. Note that you cannot specify a WAIT clause and a REGARDLESS clause on the same statement line. By specifying the WAIT clause, you can tell RMS to wait for a locked record to become available. You can optionally specify an integer expression from 0 to 255 with the WAIT clause. This integer expression indicates the number of seconds RMS should wait for a locked record to become available. If the record does not become available within the specified number of seconds, RMS signals the error "Keyboard wait exhausted" (ERR=15).

If you do not specify an integer expression with the WAIT clause, RMS waits indefinitely for the record to become available. Once the record becomes available, RMS delivers the record to the program.

Note that a deadlock condition can occur when you cause RMS to wait indefinitely for a locked record. A deadlock condition occurs when two users simultaneously try to access locked records in each other's possession. When a deadlock occurs, RMS signals the error, "RMS$_DEADLOCK". In turn, HP BASIC signals the error, "Detected deadlock error while waiting for GET or FIND" (ERR=193). To handle this error, you can either stop trying to access the particular record, or, if you must access the record, free all locked records (regardless of the channel) and then attempt the GET or FIND again. You need to unlock all records because you cannot know which record the other process wants.

Note

If the timeout value specified in the WAIT clause is less than the SYSGEN parameter DEADLOCK_WAIT, then a "Keyboard wait exhausted" (ERR=15) message can indicate that either the record did not become available during the specified time, or there is an actual deadlock situation. However, if the timeout value is greater than the SYSGEN parameter DEADLOCK_WAIT, the system correctly specifies that a deadlock situation has occurred.

The following example uses the WAIT clause to overcome a record locked condition and traps the resulting error condition:


MAP (worker) STRING first_name = 10,   &
                    last_name = 20,    &
                    badge_number = 6,  &
             LONG   dept_number

MAP (departments) STRING dept_name = 10,  &
                  LONG   dept_code
OPEN "Employee_data.dat" FOR INPUT AS FILE #1%,  &
      INDEXED FIXED, MAP worker, ACCESS MODIFY,  &
      PRIMARY badge_number

OPEN "departments.dat" FOR INPUT AS FILE #2,         &
     INDEXED FIXED, MAP departments, ACCESS MODIFY,  &
     PRIMARY dept_code

WHEN ERROR IN
   WHILE -1%
     GET #1, WAIT
     WHEN ERROR USE time_expired_handler
       GET #2%, KEY #0 EQ dept_number,  &
           WAIT 10%
     END WHEN
     PRINT badge_number, dept_name
   NEXT
USE
  SELECT ERR
    CASE = 11%
       PRINT "End of file reached"
       CLOSE 1%, 2%
    CASE = 193%
       PRINT "Deadlock detected"
       UNLOCK #2%
       RETRY
    CASE ELSE
       EXIT HANDLER
  END SELECT
END WHEN

HANDLER time_expired_handler
  IF ERR = 15% OR ERR = 193%
    THEN
       PRINT "Department info not available for:"
       PRINT "Employee ";badge_number
       PRINT "Going on to next record."
       CONTINUE
    ELSE
       EXIT HANDLER
  END IF
END HANDLER
END PROGRAM

The first WHEN ERROR block traps any deadlock conditions. The WHEN ERROR handler unlocks the current record on channel #2 in case another program is trying to access it and then retries the operation. The detached handler for the second WHEN ERROR block traps timeout errors and deadlock errors. If the desired information does not become available in the specified amount of time, or a deadlock condition occurs, the employee's badge number is printed out with an appropriate message, and the GET statement tries to retrieve the next record in the sequence.


Previous Next Contents Index