Previous | Contents | Index |
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% |
There is no current record after a deletion. The next record pointer is unchanged. |
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 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 |
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:
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).
14.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:
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.
14.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, 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.
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.
14.6.10 Accessing Records by Record File Address
A Record File Address (RFA) uniquely specifies a record in a file. Accessing records by RFA is therefore more efficient and faster than other forms of random record access.1
Because an RFA requires six bytes of storage, BASIC has a special data type, RFA, that denotes variables that contain RFA information. Variables of data type RFA can be used only with the I/O statements and functions that use RFA information, and in comparison and assignment statements. You cannot print these variables or use them in any arithmetic operation. However, you can compare RFA variables using the equal to (=) and not equal to (<>) relational operators.
You cannot create named constants of the RFA data type. However, you can assign values from one RFA variable to another, and you can use RFA variables as parameters.
Accessing a record by RFA requires the following steps:
The GETRFA function returns the RFA of the last record accessed on a channel. Therefore, you must access a record in the file with a GET, FIND, or PUT statement before using the GETRFA function. Otherwise, GETRFA returns a zero, which is an invalid RFA.
The following example declares an array of type RFA containing 100 elements. After each PUT operation, the RFA of the record is assigned to an element of the array. Once the RFA information is assigned to a program variable or array element, you can use the RFA clause on a GET or FIND statement to retrieve the record.
DECLARE RFA R_array(1 TO 100) DECLARE LONG I MAP (XYZ) STRING A = 80 OPEN "TEST.DAT" FOR OUTPUT AS FILE #1, & SEQUENTIAL, MAP XYZ FOR I = 1% TO 100% . . . PUT #1 R_array(I) = GETRFA(1%) NEXT I |
You can use the RFA clause on GET or FIND statements for any file organization; the only restriction is that the file must reside on a disk that is accessible to the node that is executing the program. An RFA value is only valid for the life of a specific version of a file. If a new version of a file is created, the RFA values might change. If you attempt to access a record with an invalid RFA value, BASIC signals a run-time error.
The following example continues the previous one. It randomly retrieves the records in a sequential file by using RFAs stored in the array.
DECLARE RFA R_array(1% TO 100%) DECLARE LONG I MAP (XYZ) STRING A = 80 OPEN "TEST.DAT" FOR OUTPUT AS FILE #1, & SEQUENTIAL, MAP XYZ FOR I = 1% TO 100% . . . PUT #1 R_array(I) = GETRFA(1%) NEXT I WHILE -1% PRINT "Which record would you like to see"; INPUT "(type a carriage return to exit)";Rec_num% EXIT PROGRAM IF Rec_num% = 0% GET #1, RFA R_array(Rec_num%) PRINT A NEXT |
The PRINT # statement transfers program data to a terminal-format file. In the following example, the INPUT statements prompt the user for three values: S_name$, Area$, and Quantity%. Once these values are entered, the PRINT # statement writes these values to a terminal-format file that is open on channel #4.
FOR I% = 1% TO 10% INPUT "Name of salesperson":S_name$ INPUT "Sales district";Area$ INPUT "Quantity sold";Quantity% PRINT #4%, S_name$, Area$, Quantity% NEXT I% |
If you do not specify an output list in the PRINT # statement, a blank
line is written to the terminal-format file. A PRINT statement without
a channel number transfers program data to a terminal. See
Chapter 6 for more information.
14.6.12 Resetting the File Position
The RESTORE # statement resets the current record pointer to the beginning of the file; it does not change the file. RESET # is a synonym for RESTORE. For example:
RESTORE #3%, KEY #2% RESET #3% |
The RESTORE # statement restores the file in terms of the second alternate key. The RESET # statement restores the file in terms of the primary key.
The RESTORE # statement can be used by all RMS file organizations.
RESTORE without a channel number resets the data pointer for READ and
DATA statements but does not affect any files.
14.6.13 Truncating Files
The SCRATCH statement is valid only on sequential files. Although you cannot delete individual records from a sequential file, you can delete all records starting with the current record through to the end of the file. In order to do this, you must first specify ACCESS SCRATCH when you open the file.
To truncate the file, locate the first record to be deleted. Once the current record pointer points to this record, execute the SCRATCH statement. The following program locates the thirty-third record and truncates the file beginning with that record.
OPEN "MMM.DAT" AS FILE #2%, & SEQUENTIAL FIXED, ACCESS SCRATCH first_bad_record = 33% FIND #2%, RECORD first_bad_record SCRATCH #2% CLOSE #2% END |
SCRATCH does not change the physical size of the file; it reduces the
amount of information contained in the file. (You can use the DCL
command SET FILE/TRUNCATE to truncate the excess file space.)
Therefore, you can write records with the PUT statement immediately
after a SCRATCH operation.
14.6.14 Renaming Files
If the security constraints permit, you can change the name or directory of a file with the NAME...AS statement. For example:
NAME "MONEY.DAT" AS "ACCOUNTS.DAT" |
This statement changes the name of the file MONEY.DAT to ACCOUNTS.DAT.
The NAME...AS statement can change only the name and directory of a file; it cannot be used to change the device name. |
You must always include an output file type because there is no
default. If you use the NAME...AS statement on an open file, the new
name does not take effect until you close the file.
14.6.15 Closing Files and Ending I/O
All programs should close files before the program terminates. However, files are automatically closed in the following situations:
Files are not closed after executing a STOP, END SUB, END FUNCTION, or END PICTURE statement.
The CLOSE statement closes files and disassociates these files and their buffers from the channel numbers. If the file is a magnetic tape device and the data is written to a tape, CLOSE writes trailer labels at the end of the file. The following is an example of the CLOSE statement:
CLOSE #1% B% = 4% CLOSE #2%, B%, 7% CLOSE I% FOR I% = 1% TO 20% |
If the security constraints permit, you can delete a file with the KILL statement. For example:
KILL "TEST.DAT" |
This statement deletes the file named TEST.DAT. Note that this statement deletes only the most current version of the file. Do not omit the file type, because there is no default. You can delete only one file at a time; to delete all versions of a file matching a file specification, use the Run-Time Library routine LIB$DELETE_FILE.
You can delete a file that is currently being accessed by other users; however, the file is not deleted until all users have closed it. You cannot open or access a file once you have deleted it.
1 Record File Addresses do not exist for terminal-format files. |
The following built-in functions are provided for finding:
These functions are discussed in the following sections.
14.7.1 FSP$ Function
If you do not know the organization of a file, you can find out by opening the file for input with the ORGANIZATION UNDEFINED and RECORDTYPE ANY clauses. Your program can then use the FSP$ function to determine the characteristics of that file. Your program must execute FSP$ immediately after the OPEN FOR INPUT statement. For example:
RECORD FSP_data VARIANT CASE BYTE Org BYTE Rat WORD Max_record_size LONG File_size WORD Bucketsize_blocksize WORD Num_keys LONG Max_record_number CASE STRING Ret_string = 16 END VARIANT END RECORD DECLARE FSP_data File_chars OPEN "FIL.DAT" FOR INPUT AS FILE #1%, & ORGANIZATION UNDEFINED, & RECORDTYPE ANY, ACCESS READ File_chars::Ret_string = FSP$(1%) |
The following list explains the above example:
Note that FSP$ returns zeros in bytes 9 to 12. For more information, see the OpenVMS Record Management Services Reference Manual.
Previous | Next | Contents | Index |