MFS PROGRAMMING & USAGE
Published By | Date | Version | Knowledge Level | Keywords |
---|---|---|---|---|
Revelation Technologies | 14 NOV 1989 | 2.X | EXPERT | %RECORDS%, @-variables, @LIST.ACTIVE, @NEXT.FS, sample, program, Next, Aborting, Disabling, Accessing, file+, also, Calling,, Direct, call, different, files, Accounts,, Non-SYSPROG, Arguments, modifying+, names, Assembly, language, Attaching, after, changing, file, handle, installation, using, volume, name, Audit, trail, calling, system, argument, during, FLUSH, INSTALL, BFS,, definition, BFS-bound, Branching+, Btree, index, Buffers, caching, Bypassing, with, direct, calls, Caching, Calling, directly, from, R/BASIC, next, list, CASE, statement, Cataloging, Changing, CLEARSELECT, CODE, Compatibility, Compiling, Compression, CREATE.FILE, CREATE.MEDIA, Current, accessing, Cursors, Data, passing, De-installing, object, code, Debugger, Debugging,during, development, Decryption, Encryption, DELETE.FILE, Detaching, DICT.MFS, Dictionary, problems, with+, current, Direction,READNEXT, function+, Dispatching+, Environmental, Bond, Example |
MFS's - PART V
SELECT and READNEXT Calls
Programmers familiar with the normal flow of logic in SELECT and READNEXT operations within an R/BASIC program will find that these do not always translate directly into equivalent filing system calls. The following discussion addresses the function of the filing system SELECT and READNEXT calls, and their relationship to R/BASIC commands of the same name.
SELECT and READNEXT Processors
Much activity that programmers associate with select list processing – initializing select lists (cursors), fetching record keys, sorting, and selecting (reducing) – is done not by the filing system, but by special SELECT and READNEXT processors. These processors, a set of system programs that provide a layer between R/BASIC and the filing system, are the master controllers for select list processing. It is only under the control of these processors that R/BASIC will access the filing system for SELECT and READNEXT processing.
The special SELECT and READNEXT processors automatically take care of the more complex aspects of select list processing such as sorting or reducing. In comparison, the filing system SELECT and READNEXT calls are relatively simple. In most cases, they simply return a group of record keys or records, as directed by the higher-level SELECT and READNEXT processors. Decisions about placing record keys in cursors or sorting blocks of records for extended SELECT logic are made by the special processors.
As a rule, MFS programmers do not need to concern themselves with these esoteric aspects of select list processing. In some cases – for instance, in SI.MFS – the MFS deliberately assumes control over specific tasks (in this case, returning sorted key lists). However, an MFS that does not concern the return of a sorted or reduced key list does not need to include logic for these tasks.
Simple (Latent) Select
In R/BASIC, a simple SELECT command (not a PERFORM "SELECT …" command, nor an extended SELECT … BY command) does not directly produce a list of record keys for processing. Instead, the SELECT command simply initializes variables in the system, indicating that a select condition is active.
In addition, the SELECT is passed to the filing system so it can perform any filing system-specific activity to initialize a select condition. A filing system SELECT call returns a select mode with three possible values in the RECORD argument:
Select Mode | Meaning |
---|---|
0 | No select list is active (disable the select) |
1 | Latent file select |
2 | Latent index select |
statements will return record keys until the file has been exhausted. If no select list is active, a subsequent R/BASIC READNEXT statement will fall through its ELSE branch.
The select mode value is available to an R/BASIC program in the system variable @LIST.ACTIVE. If the SELECT call fails for any reason, the RECORD argument is passed back false, indicating to the system that no select list is active. @LIST.ACTIVE will also be set to false.
Note: for more information about latent and resolved select lists, see the Advanced Revelation documentation.
A simple R/BASIC program fragment illustrates the typical flow of a simple SELECT:
SELECT FILE ; * establish "latent" select DONE = 0 LOOP READNEXT @ID ELSE DONE = 1 UNTIL DONE DO (etc.) REPEAT
READNEXT
Once a select condition is established (select mode is true), the system can use a READNEXT command to return record keys from the select list. In R/BASIC, READNEXT is used to return a single key from the select list. In a filing system, however, a READNEXT call returns an entire block of keys.
When a READNEXT operation is being performed at the R/BASIC level, requests for record keys are processed by the READNEXT processor. The processor maintains a system list variable (cursor) for currently active keys. When a program first executes an R/BASIC READNEXT, this list variable is typically empty.
If the list variable is empty, the READNEXT processor generates a filing system READNEXT operation. The filing system READNEXT returns a block of keys, which the processor stores in the system list variable.
The READNEXT processor satisfies requests for individual record keys from this list variable. When the READNEXT processor reaches the end of the list of keys in the list variable, the READNEXT processor generates another BFS READNEXT call. The filing system READNEXT call returns the next block of keys, which replaces the keys in the system list variable. An R/BASIC READNEXT command may thus execute many times before causing the READNEXT processor to execute a filing system READNEXT operation.
This cycle of filling the list variable, exhausting it, and generating a new filing system READNEXT operation continues until the filing system READNEXT indicates that there are no more blocks of keys in the file. At that point, the filing system READNEXT sets the status argument to false. This causes the R/BASIC READNEXT operation to fall through its ELSE logic.
In order to track its progress through the file, the filing system READNEXT operation maintains a select pointer. This is a type of handle that contains the information that the filing system READNEXT requires in order to fetch the next block of keys properly. A simple select pointer might contain a pointer to the next block or group of keys to be fetched. As each filing system READNEXT operation is called, the select pointer is incremented.
Note: an MFS should not disturb a select pointer for which it is not responsible. Doing so can cause severe errors in the key blocks returned by a filing system READNEXT operation, or can result in logical read errors in the file.
READNEXT Direction
When a filing system READNEXT call is made, the status argument is used to pass to the filing system the direction of the READNEXT operation (ascending or descending). The filing system uses this direction in maintaining the select pointer. If the R/BASIC READNEXT operation designates the select list as descending, the select pointer is decremented with each filing system READNEXT operation, rather than being incremented for an ascending READNEXT.
MFS and READNEXT
One reason an MFS may wish to trap the READNEXT call is in order to fulfill READNEXT calls without recourse to the BFS. In Advanced Revelation, both QUICKDEX.MFS and SI.MFS trap the READNEXT call, returning record keys without passing the call to the BFS.
QUICKDEX.MFS provides a good model for using an MFS to trap READNEXT requests. If the filing system SELECT call has returned a select mode of 1 (latent file select), QUICKDEX.MFS traps the READNEXT call. The MFS reads the %RECORDS% record from the current file, and returns it as the next block of keys. If another READNEXT call comes down to the MFS, QUICKDEX.MFS simply returns a false, since all keys were returned in the first call.
Extended (Resolved) Select
Unlike the simple SELECT statement, the extended R/BASIC SELECT … BY statement results in a resolved list of record keys. Instead of simply initializing variables, the special SELECT processor executes a series of operations. Only when the operations are completed and when a list of record keys is available does the R/BASIC program proceed to the next statement.
The R/BASIC logic that generates a resolved list looks something like this:
SELECT "FILENAME" BY "FIELD1" SETTING 0 ELSE MSG("Select failed with status ":STATUS(), '','','') STOP END * resolved list now available * (no BFS access required) DONE = 0 LOOP READNEXT @ID ELSE DONE = 1 UNTIL DONE DO (etc.) REPEAT
In order to resolve the select list, the special SELECT processor must examine each record in the file (or index). As a result, SELECT … BY generates a series of filing system calls. In effect, the single R/BASIC SELECT statement compresses an entire SELECT … READNEXT … READ loop into a single statement, examining each record in the file.
When the READNEXT loop is completed and all records have been assembled, the SELECT processor sorts the records. The result is a resolved select list, with the keys in order according to sort criteria specified in the SELECT … BY statement. The entire select list is then available in the system list variable and @LIST.ACTIVE is set to 3.
Because the SELECT … BY statement has completely resolved the select list, no further filing system calls are required. The READNEXT processor simply fetches the sorted keys from the system list variable, without requiring filing system READNEXT calls.
Index-Based Select
If the sort criteria passed in an extended R/BASIC SELECT statement represent indexed fields, there is no need for the SELECT processor to execute an entire SELECT … READNEXT … READ loop in order to build a resolved list of keys. Instead, the list can be constructed directly from the Btree index.
The result of this condition is a latent index-based select. This is similar to a latent file-based select condition as established by a simple R/BASIC SELECT. However, it differs in that a latent index-based select can return keys in sorted order.
A latent index-based select condition is not a BFS-level function. If an indexed field is passed as a sort criterion in a filing system SELECT call, SI.MFS traps the call. Other MFSs that follow SI.MFS, and the actual BFS, never receive the SELECT call.
SI.MFS returns a select mode of 2 in the RECORD argument (causing @LIST.ACTIVE to be set to 2), indicating an index-based select condition. SI.MFS will then trap subsequent filing system READNEXT calls, providing blocks of keys out of the Btree index. Again, filing systems that follow SI.MFS will never receive this call, since it is trapped and fulfilled by SI.MFS.
CLEARSELECT
A special call is provided to enable a filing system to clear select lists. The major function of a CLEARSELECT is to clear the system variables (such as the system list variable) used to indicate an active select list.
The call is passed through to the filing system, however, in case the filing system needs to take action when a select list condition is cleared. For example, Linear Hash (RTP57) uses a CLEARSELECT call as an indication that it can reset its sizelock parameter by decrementing it.
OPEN.FILE Call and File Handles
MFS programmers will notice quickly that there is not a one-to-one correspondence between an R/BASIC OPEN statement and an MFS-level OPEN.FILE call. In fact, as a rule, an OPEN.FILE call will be generated only once per session for a given file.
OPEN.FILE Call
The OPEN.FILE call is generated by the R/BASIC interpreter in order to create a file handle for the file. The BFS creates a handle that it will recognize for subsequent read, write, and other record-oriented requests. The format of the file handle is dependent on the BFS being accessed.
Once the file handle has been created and returned from the BFS, the R/BASIC interpreter stores it in field five of the FILES file entry for the file in question. Any file that has already been opened during the current Advanced Revelation session will have a file handle in this field.
If the R/BASIC interpreter detects a file handle in the FILES file for the file being accessed, it will not generate a call to the filing system to return a handle. Instead, the handle is simply read from the FILES file and returned to the R/BASIC OPEN statement. This results in greater efficiency by lowering the overhead required to return a file handle.
Note: for more information about the FILES file, see Appendix 1.
R/BASIC OPEN File Handle
The file handle returned in the R/BASIC OPEN statement is not identical to that stored in the FILES file. R/BASIC OPEN returns a file handle that contains both the filing system list for a file (the MFS and BFS list) as well as the file handle required by the BFS. The filing system list is the first value (@VM-delimited) of the returned handle, and the BFS file handle is the second value. Multiple MFSs in the file handle are delimited with @SVM.
For example, the BFS EX.BFS might return a file handle looking like this: \DATA\REV10000. If the file has SI.MFS and SAMPLE.MFS installed, the file handle returned by the R/BASIC OPEN statement will be:
SI.MFSnSAMPLE.MFSnEX.BFS2\DATA\REV10000
Updating the File Handle
One implication of storing the file handle is that the handle and MFS information stored in the FILES file must be updated if anything about a file changes that would normally affect the file handle. A common example is the addition or removal of an MFS to a file. If an MFS is added to a file, the entry in the FILES file must reflect this change.
The FILES file is write-protected. As a result, changes to the file handle must be registered indirectly. In essence, the programmer must force the system to clear the file handle in the FILES file, and thereby guarantee that the R/BASIC interpreter generate an OPEN.FILE call to the filing system the next time the file is accessed.
To do so, the programmer should detach and then re-attach the file after making appropriate changes such as installing the MFS.
OPEN Failure
It is rare that an R/BASIC OPEN statement will fail (fall through its ELSE logic) as a result of a BFS OPEN.FILE call. Within an R/BASIC program, a failure in an OPEN statement (ELSE logic) is likeliest because the file has not been attached.
If the file is attached but has not yet been opened, a filing system OPEN.FILE call is made. The BFS can choose to create a file handle in any way it likes, but will typically examine the media map for the volume in question, and read the physical file for information required for the file handle.
At this point, an OPEN will fail only if the information in the media map does not correspond to the file in some way, or if the media map itself had changed since the volume as a whole had been read by the ATTACH command. For example, if a media map entry pointed to a DOS (or other operating system) file that did not exist, the filing system will likely detect this mismatch. The OPEN will thus fail at the BFS level; this is indicated by a return value of false in the STATUS variable.