________________________________________________________________________________ ________________________________________________________________________________ Contributions ________________________________________________________________________________ ________________________________________________________________________________ Submissions to this newsletter are constantly sought. A submission can be an article, a letter to the Wombat Wizard, a technical tip, or anything of interest to people using or considering the use of Datatrieve or any 4GL product. Submissions on magnetic media are preferred but almost any type will be considered. Contributions for the newsletter can be sent to either of the following addresses: Editor, DATATRIEVE Newsletter Joseph Gallagher, Ph.D. c/o DECUS U.S. Chapter Research Medical Center 219 Boston Post Road, BP02 2316 East Meyer Blvd. Marlboro, MA 01752 Kansas City, MO 64132 ________________________________________________________________________________ ________________________________________________________________________________ Table of Contents ________________________________________________________________________________ DECUS U.S. Chapter SIG Newsletters, Volume 3, No. 8, Apr. 1988 Wombat Examiner and 4GL Dispatch, Volume 9, No. 8 DTR-2 - Context Recognition in VAX Datatrieve DTR-22 - The Story of P DTR-25 - Documenting and Manupulating SYSGEN Parameters using Datatrieve DTR-29 - Wombat Wizard Errata ________________________________________________________________________________ ________________________________________________________________________________ ________________________________________________________________________________ CONTEXT RECOGNITION IN VAX DATATRIEVE Donna Brown and Sue Harris - Digital Equipment Corp., Nashua, NH ________________________________________________________________________________ 1 OVERVIEW This paper will describe context resolution within DATATRIEVE. It explains the impact context has on you as a DATATRIEVE user. It covers how you specify context within DATATRIEVE as well as common pitfalls in the area of DATATRIEVE context. It explores the internals of context resolution in DATATRIEVE. 2 WHAT IS CONTEXT? Context is described as the set of mechanisms by which DATATRIEVE recognizes field names and identifies target records for statements. Changing the context in which a DATATRIEVE statement executes can change the statement's outcome. This is why it is important for you to understand context's impact upon DATATRIEVE statements. Context answers questions such as "which one" and "how many". For example, assume that the statement "MODIFY EMP_NUM" modifies employee numbers from personnel records, such as those illustrated below. We know that the EMP_NUM field is changed, but which record is modified? How many records are modified, one or many? These are the types of questions that DATATRIEVE resolves using context. --------- --------- --------- | Harris|.. | Jones |.. | Brown |. . . . | 7544 | | 7329 | | 7542 | | | | | | | --------- --------- --------- DTR> MODIFY EMP_NUM 2.1 WHY IS CONTEXT IMPORTANT? The following sections describe the part context plays within DATATRIEVE. 2.1.1 Naming DATATRIEVE does not require that every identifier name used within a DATATRIEVE session be unique. The following example shows that two different records can contain fields with the same name: DEFINE RECORD A USING DEFINE RECORD B USING 01 TOP. 01 TOP. ----> 03 EMP_NUM PIC XX. 03 EMP_NUM PIC 999. 03 ADDRESS PIC X(30).; 03 SUPERVISOR PIC X(20).; Both records contain EMP_NUM fields. To which field does the following statement refer? DTR> MODIFY EMP_NUM Because there are two fields named EMP_NUM, DATATRIEVE must take into account factors other than the field name when deciding which field to modify. DATATRIEVE follows a set of conventions when deciding which field to act upon. These conventions are the rules of context resolution. By understanding these rules, you will be better able to anticipate the behavior of the DATATRIEVE statements you enter. 2.1.2 List Fields In the following FAMILY record, the field KIDS is defined with an OCCURS clause. Each FAMILY record thus contains a "LIST" of kids. This LIST can contain information pertaining to one kid up to ten kids, or the list may be empty. 01 FAMILY. : 03 NUMBER_KIDS PIC 99 EDIT_STRING IS Z9. 03 KIDS OCCURS 0 TO 10 TIMES DEPENDING ON NUMBER_KIDS. 06 EACH_KID. 09 KID_NAME PIC X(10) QUERY_NAME IS KID. : The following diagram shows that for each FAMILY record there can be a variable number of KIDS fields. The first record contains 2 KIDS the second one kid, etc. --------- --------- --------- -------- | Harris|.. | Jones |.. | Brown |.. | White| . . . . | 02 | | 01 | | 03 | | 02 | | Bob | | Betty| | Tom | | Alex| | Alice| | | | Dick | | Jen | | | | | | Harry| | | --------- --------- --------- -------- Context resolution becomes more complex when you use list records. Where a list is involved, there are even more possible choices. Besides answering the question "which family" DATATRIEVE must also answer the question "which kid"? For example, the following PRINT statement refers to the list field KIDS. DTR> PRINT ALL KIDS But, each record may have more than one KIDS field. Which KIDS field should be printed? ALL occurrences of KIDS for a particular record? ALL occurrences of all KIDS for all records? We will look at these question in more detail in a later section. 2.1.3 Control Of DATATRIEVE Statements If you enter a statement or sequence of statements incorrectly, DATATRIEVE may not have enough information to determine which field a name in a statement refers to. You can tell when this happens, because DATATRIEVE displays the familiar message: "<...> is undefined or used out of context" What happens if you do not correctly express the context of your statement, but your statement can be interpreted in another context within your DATATRIEVE session? In this case, an error message will not be produced. DATATRIEVE will execute your statement, but in a different manner than you had intended. The results can be surprising and may even change data in ways that you do not expect. This is certainly not a desirable situation. By understanding context resolution in DATATRIEVE and by specifying all context explicitly you can avoid this type of accident. 2.2 TYPES OF CONTEXT RECOGNITION There are two levels of context recognition within DATATRIEVE. They are IMPLICIT context recognition and EXPLICIT context recognition. Implicit means that context is implied by the environment in which a statement is executed. Explicit means that the context for a statement is entered as part of that statement. 2.2.1 Implicit Context Recognition DATATRIEVE is sometimes said to be "context-sensitive". This is because the meaning of a given DATATRIEVE statement varies depending on the environment in which it is executed. This is an important aspect of the DATATRIEVE language. It gives power and flexibility not seen in third generation programming languages. NOTE In this discussion, we are not referring to the mathematical linguistic concept of context-sensitive, as it applies to classification of grammars. We use this terminology (as well as "left context" and "right context") in a more intuitive and informal way, as a method of highlighting and explaining a particular characteristic of VAX DATATRIEVE. The following example shows how the environment set up by different DATATRIEVE commands causes the statement "PRINT XYZ" to produce different results. In the first example, a character string is printed. In the second, a number is printed. And in the last example multiple fields of different types are printed. -------------------------- ------------------------------- | Context 1 (field): | | Context 2 (variable): | | ====================== | |=============================| | DTR>READY DOMAIN1 | | DTR>DECLARE XYZ PIC 9V9. | | DTR>SHOW FIELDS | | DTR>XYZ = 3.2 | | : | | | | | | | | XYZ | | | | | | | | DTR>FIND DOMAIN1 | | | | DTR>PRINT XYZ | | DTR>PRINT XYZ | | | | | | XYZ | | XYZ | | | | | | "ALF " | | 3.2 | | | | | -------------------------- ------------------------------- ---------------------------------------- | Context 3 (domain): | |======================================| | DTR>READY XYZ | | DTR>PRINT XYZ | | | | Field1 Field2 Field3 | | 11 abc .01 | | 22 def .02 | | 33 ghi .03 | | | | etc. | ---------------------------------------- So you see, the output of "PRINT XYZ" can be greatly altered by the environment in which it executes. That environment is determined by the commands preceding the statement "PRINT XYZ". Implicit context recognition is at work in each of these examples. The context-sensitive nature of DATATRIEVE has a number of implications. One is that DATATRIEVE procedures are not compiled. The previous example illustrates why this is so. Suppose "PRINT XYZ" were in a procedure. What would it mean to compile that procedure without knowing the datatype of XYZ? Without even knowing whether XYZ is the name of a domain, field or variable? The high degree of flexibility provided by DATATRIEVE limits the assumptions that can be made at compilation time. Context sensitivity is also the reason that the distinction between "commands" and "statements" exists in DATATRIEVE. Commands set up the context in which statements execute. Because of this, restrictions exist in the way commands can be used (see the VAX DATATRIEVE Reference Manual for a list of commands and statements.) One restriction is that commands cannot be used within a compound statement. In the following example, the REPEAT loop is a compound statement. The READY command within the REPEAT loop is therefore illegal: DTR> DECLARE XYZ LONG. DTR> REPEAT 5 CON> BEGIN CON> PRINT XYZ CON> READY XYZ ! this is NOT allowed!!! CON> END "Expected statement, encountered "READY". If DATATRIEVE allowed commands within compound statements, the first iteration of the above REPEAT loop would print the variable XYZ, and subsequent iterations would print the entire domain of XYZ. Besides being confusing, this would create technical difficulties. Enforcing a stable environment for a compound statement allows DATATRIEVE to make decisions about the type and size of the data being processed just once at the start of processing of that compound statement. If commands were permitted inside of compound statements, then decisions about data type and size would have to be made during each iteration of a compound statement, thereby increasing execution time. Context sensitivity also contributes a more "English-like" feeling to the DATATRIEVE language. As in the English language, certain ambiguities can occur, but in DATATRIEVE these can be resolved through the context mechanism. 2.2.2 Explicit Context Recognition There may be times when you need to override implicit context recognition in DATATRIEVE in order to get the results you desire. You do this using explicit context specification within your DATATRIEVE statements. By using explicit context specification as a general practice, you will be sure to always get the data that you intend to. The example below shows a case where XYZ has two possible meanings. XYZ can refer to either a variable or to a field within the domain DOMAIN1. If no special direction is given, the variable XYZ will be printed. DTR> READY DOMAIN1 DTR> DECLARE XYZ PIC 9V9. DTR> XYZ = 3.2 : DTR> PRINT XYZ XYZ 3.2 To print the field XYZ, DATATRIEVE must be instructed to obtain the value for XYZ from DOMAIN1. This is done below using the clause "OF DOMAIN1". This is an example of explicit context recognition. DTR> PRINT XYZ OF DOMAIN1 XYZ "ALF" Other methods of explicit context recognition will be discussed in a later section. 3 Using DATATRIEVE Context Context recognition (both implicit and explicit) performs two different functions within DATATRIEVE. These functions are Target Record Identification, which answers the question "how many", and Name Recognition, which answers the question "which one". 3.1 Target Record Identification Context determines which records will be the target of statements such as PRINT, MODIFY and ERASE. That is, which records are printed, modified or erased. How does DATATRIEVE identify the target records for a particular operation? Target records for PRINT, MODIFY and ERASE statements are determined in part by the form of the statement you enter. The syntax of these statements provides three basic context specifications. Specification of context in a statement can be implicit or explicit. By implicit we mean that the target record is not specified in the statement syntax, but is implied by the environment in which the statement executes. Explicit specification means that the target record is specified in the statement syntax. The target of a PRINT, MODIFY or ERASE may be a single record, a collection of records, or a stream of records. The syntax of a statement determines the number of records operated on. That is, the syntax may imply either single-record context or multiple-record context. These different options are shown in the following examples: 1. IMPLICIT, SINGLE-RECORD CONTEXT No context is specified; it is implicit. Only one record will be modified. DTR> MODIFY 2. PARTIALLY EXPLICIT, MULTIPLE-RECORD CONTEXT Context is expressed by the keyword "ALL" rather than by an explicit RSE (record selection expression.) A group of records will be modified. DTR> ERASE ALL 3. EXPLICIT, MULTIPLE-RECORD CONTEXT Context is detailed in the "OF RSE" clause. A group of records will be modified. DTR> MODIFY PRICE OF YACHTS Each of these categories are detailed below. 3.1.1 Single-Record Context There are different ways to specify a single-record as the target of a statement. One is by means of a "selected record". The SELECT statement identifies one target record in a collection. A record is selected by first forming a "collection" of records using the FIND statement and then choosing a particular record from that collection using the SELECT statement. In the example below, a collection of all of the YACHTS records is formed using a FIND statement. The first record of the collection is then selected. Since a target is not explicitly specified on the PRINT statement, the selected record will be printed. DTR> FIND YACHTS DTR> SELECT DTR> PRINT Another method of specifying single-record context is the FOR loop. A FOR loop identifies individual records of a record stream, in turn. It may seem that the FOR construct actually provides multiple-record context, because it produces a stream of records. However, there is only one target record per iteration of a FOR loop. The FOR construct allows you to work with many records at once, while dealing with each record individually. Therefore, the single-record form of a statement is used within a FOR loop to operate on the target record of the FOR loop. In the example below, all of the records from the YACHTS domain will be printed. Notice that the single-record context form of the print statement is used within the FOR loop. DTR> FOR YACHTS CON> PRINT What if two possible targets, one a selected record and one the target of a FOR loop, are available at once? Which one will be used? If more than one record qualifies as a target record, then DATATRIEVE will always use the most recent context. In the example below, the stream of records from the domain OWNERS will be printed, rather than the selected YACHTS record. DTR> FIND YACHTS DTR> SELECT DTR> FOR OWNERS CON> PRINT 3.1.2 Multiple-Record Context You can specify multiple record context in one of two ways. One way is to use the keyword "ALL", without an RSE, on the command line. When used in this way, ALL identifies all of the records in the current collection as the target of the statement. In the example below, the FIND forms a collection containing the first two records in the domain YACHTS. The keyword ALL on the PRINT and ERASE statements specify both records in the current collection as targets. After the ERASE statement is executed, both of the records in the collection are erased. Therefore, the second PRINT does not display any records. DTR> FIND FIRST 2 YACHTS DTR> PRINT ALL LENGTH OVER MANUFACTURER MODEL RIG ALL WEIGHT BEAM PRICE ALBERG 37 MK II KETCH 37 20,000 12 $36,951 ALBIN 79 SLOOP 26 4,200 10 $17,900 DTR> ERASE ALL DTR> PRINT ALL You can also specify multiple record context using the "OF RSE" clause. The "OF RSE" clause identifies a target record stream made up of records meeting the conditions set in the RSE. In the example below, the "OF YACHTS" clause on the MODIFY statement specifies that the record stream made up of all the records from the YACHTS domain will be modified. The result is that the PRICE field of every record in YACHTS is changed to 0. DTR> MODIFY PRICE OF YACHTS Enter PRICE: 0 DTR> Contrast this with the single-record context produced by the FOR loop below. Each record is handled individually and you are prompted for a PRICE value for each record in the record stream. You should use extreme caution when utilizing a multiple-record context technique (such as OF RSE) to avoid inadvertently modifying all of the records in a domain at once when you actually want to modify the records individually. DTR> FOR YACHTS MODIFY PRICE Enter PRICE: 33000 Enter PRICE: 21000 etc. (for each record) 3.2 Name Recognition Given a statement such as: PRINT PRICE DATATRIEVE must determine what is meant by PRICE. Is PRICE a domain, a variable, or a field? If PRICE is a field, which domain is it from? What if more than one readied source has a PRICE field? DATATRIEVE determines the answer to these questions through the process of name recognition. DATATRIEVE uses two methods of name recognition: implicit and explicit. 3.2.1 Implicit Name Recognition DATATRIEVE keeps track of possible contexts using a stack. A stack is an internal data structure which is manipulated in such a way that the most recently added items are the first to be accessed (last on, first off.) DATATRIEVE uses the stack to resolve context. When DATATRIEVE encounters a name in a DATATRIEVE statement, the stack is searched for that name. If the stack contains more than one entry for a particular name, the name from the most recent context will be used. In the example below, the FIND and SELECT statements make entries on the stack. When the PRINT statement is executed, DATATRIEVE looks for a context for the name TYPE. Looking on the stack, the name TYPE is found to be a field from the selected record. DTR> READY YACHTS DTR> FIND YACHTS WITH PRICE NE 0 DTR> SELECT FIRST DTR> PRINT TYPE In the example below, stack entries are again made as a result of FIND and SELECT statements. A stack entry for OWNERS is also made as a result of the FOR statement. When the PRINT statement is executed, OWNERS is on top of the stack. The OWNERS record does not contain a PRICE field though. So, DATATRIEVE looks further down the stack until a reference to PRICE is found. In this case, PRICE is found in the selected YACHTS record. That single PRICE field will be printed 18 times, once for each record in OWNERS. DTR> FIND YACHTS DTR> SELECT FIRST DTR> FOR OWNERS CON> PRINT PRICE What is the ordering on the stack? What is meant by most recent or "closest" context? The answer to these questions is generally intuitive. The diagram below outlines the arrangement of the stack. The diagram shows that the environment set up by the statement currently being executed will be considered before a selected record, a collection, or global variables. TOP ---------------------------------------------------- | | | Context blocks created by the statement: | | ("innermost" blocks at top of stack) | | | | RECORD STREAMS | LOCAL | VERIFY | VALID IF | | | VARIABLES | (STORE) | | | | | | | ---------------------------------------------------- | CURRENT collection (if a SELECTed record) | ---------------------------------------------------- | Named collections with SELECTed records | | (in same order as SHOW COLLECTIONS display) | ---------------------------------------------------- | Global variables | ---------------------------------------------------- BOTTOM This diagram shows that context from the innermost statement is placed on top of the stack. Innermost refers to the statement which is the most nested of a group of statements. In the example below, the innermost statements are those within the compound statement under the FOR OWNERS statement. The stack diagram shows that the context from the outer FOR YACHTS loop is placed on the stack followed by the context set up by the FOR OWNERS statement. The variable INNER1 declared in the innermost statement is placed on top of the stack last. DTR> FOR YACHTS BEGIN DECLARE OUTER1 PIC X(5). FOR OWNERS BEGIN DECLARE INNER1 PIC 999V9. -----------> INNER1 = 2.2 END END This is what the context stack looks like at the time of the assignment of 2.2 to INNER1: TOP ---------------------------------------------------- | | | INNER1 context | | OWNERS context | | OUTER1 context | | YACHTS context | ---------------------------------------------------- | CURRENT collection (if a SELECTed record) | ---------------------------------------------------- | Named collections with SELECTed records | | (in same order as SHOW COLLECTIONS display) | ---------------------------------------------------- | Global variables | ---------------------------------------------------- BOTTOM DATATRIEVE actually uses two stacks to keep track of context. The first stack, the general context stack, keeps track of possible name resolutions for most DATATRIEVE statements. The second stack, the update stack, keeps track of fields which are possible targets for updates or assignments. DATATRIEVE checks the update stack to ensure that you assign only to those fields that can accept updates, that is, a field which is part of a record which is being stored or modified, or a variable. The table below shows that a field will be placed on the update stack if the field is named in a STORE or MODIFY statement or is part of a record named in a STORE or MODIFY statement, or if the field is a declared variable. Local variables and the targets of STOREs and MODIFYs remain on the update stack only for the duration of the statements of which they are a part. CONTEXTS PLACED ON THE UPDATE STACK CONTEXT TYPE CONTEXT SCOPE --------------------------------------------------------------- | STORE USING | TARGET OF STORE | STORE USING | --------------------------------------------------------------- | MODIFY USING | TARGET OF MODIFY | MODIFY USING | --------------------------------------------------------------- | LOCAL VARIABLE | VARIABLE | BEGIN-END BLOCK | --------------------------------------------------------------- | GLOBAL VARIABLE | VARIABLE | ANY STATEMENT | --------------------------------------------------------------- The table above shows that a source referenced in a STORE statement is placed on the update stack. The context for the source being stored is not placed on the general context stack, however. In the statement below for example, OLD_YACHTS is placed only on the update context stack. NEW_YACHTS is placed on the general context stack. So, even though both the source and destination records contain BOAT fields, DATATRIEVE will be able to determine the correct domain by referring to the appropriate stack. DTR> FOR OLD-YACHTS STORE NEW-YACHTS USING BOAT = BOAT You may sometimes see the general context stack referred to as the Right context stack and the update context stack referred to as the Left context stack. The ordering rules for the update stack are basically the same as those for the general stack. The difference is that fewer entries are made on the update stack. As shown below, only variable declarations and STORE and MODIFY statements make entries on the update stack. ORDERING OF THE UPDATE CONTEXT STACK TOP ---------------------------------------------------- | | | Context blocks created by the statement: | | (innermost blocks at top of stack) | | | | STORE | MODIFY | LOCAL VARIABLES | | | | | ---------------------------------------------------- | Global variables | ---------------------------------------------------- BOTTOM The example below shows the ordering of both context stacks. The state of the stacks at the time the of the STORE statement (indicated by the arrow) is shown. YACHTS is pushed onto both stacks when the FOR YACHTS MODIFY statement is executed, and will remain on both stacks until the FOR loop ends. OWNERS is pushed onto the the update stack each time the STORE statement is executed and is popped off when the store is completed. The local variable NEW_MODEL is placed on both stacks at the time it is declared and remains until the termination of the FOR loop. DTR> FOR YACHTS MODIFY USING BEGIN PRICE = PRICE * 2 DECLARE NEW_MODEL PIC X(10). NEW_MODEL = *."new model" STORE OWNERS USING --------------> MODEL = NEW_MODEL END STACK CONTENTS AT TIME OF ASSIGN Left Context stack Right Context Stack (Update Context stack) (General Context Stack) ------------------------- ------------------------------------- | OWNERS context | | NEW_MODEL context | ------------------------- ------------------------------------- | NEW_MODEL context | | YACHTS context | ------------------------- ------------------------------------- | YACHTS context | | Collections (with SELECTed record)| ------------------------- ------------------------------------- | Global variables | | Global variables | ------------------------- ------------------------------------- 3.2.2 Explicit Name Recognition The previous section discussed how name recognition takes place automatically in DATATRIEVE. Now we will discuss how you can override DATATRIEVE's implicit name recognition by specifying the name explicitly. There are two ways of forcing name recognition. One is by using name qualification and the other is by using context variables. 3.2.2.1 NAME QUALIFICATION Name qualification allows you to override the context established by DATATRIEVE's implicit name recognition. This is done by providing a fully qualified name in a DATATRIEVE statement. A fully qualified name includes a specification for the readied source (the domain name or database name), all group fields of which the field is a part, and the field name itself. A portion of the record definition for the record YACHT is shown below. If a readied domain called YACHTS references the record YACHT, then the fully qualified name for the field MODEL is: YACHTS.YACHT.BOAT.TYPE.MODEL. RECORD YACHT 01 BOAT. 03 TYPE. 05 MANUFACTURER PIC X(10) QUERY_NAME BUILDER. 05 MODEL PIC X(10). 03 SPECIFICATIONS : : You should use name qualification when DATATRIEVE's default implicit qualification is not what you want. For example, suppose that you want to print a list of yachts and for each yacht list all of the owners who own that type of yacht. You might try the following: DTR> FOR YACHTS FOR OWNERS WITH TYPE EQ TYPE PRINT TYPE, NAME This will not produce the desired results, however. The problem lies in the RSE "TYPE EQ TYPE". The intent is to find owners with TYPE equal to TYPE from YACHTS. But, since OWNERS is higher on the context stack than YACHTS, both references to TYPE in the RSE will resolve to OWNERS. You must force DATATRIEVE to resolve one of the TYPEs to YACHTS. You can do this by qualifying TYPE, as in the following example. Note that TYPE does not have to be fully qualified, just enough to determine uniqueness. DTR> FOR YACHTS FOR OWNERS WITH TYPE EQ YACHTS.TYPE PRINT TYPE, NAME 3.2.2.2 Context Variables Name qualification can be used to differentiate between fields of the same name from different domains. But what if the duplicate names are from the same domain? For example, suppose you want to print a list of builders who build boats of more than one kind of rig. This requires comparing the RIG field of one YACHTS record to the RIG field of another YACHTS record. You might try the following: DTR> FOR YACHTS CON> FOR YACHTS WITH BUILDER EQ BUILDER AND RIG NE RIG CON> PRINT BUILDER, RIG, RIG DTR> The problem again is that context resolves to the inner FOR loop. So, BUILDER will be compared to BUILDER from the same record and RIG will be compared to RIG from the same record. Using the domain name will not help, because YACHTS is the domain name in both cases. So, how can you distinguish BUILDERS and RIG? You can use context variables to distinguish names from the same domain. In the following example, the context variable A is supplied for the outer FOR loop and B for the inner FOR loop. The context variables are then used within the RSE to qualify BUILDER and RIG. DTR> FOR A IN YACHTS CON> FOR B IN YACHTS WITH B.BUILDER EQ A.BUILDER AND CON> B.RIG NE A.RIG CON> PRINT BUILDER, A.RIG, B.RIG A context variable is an artificial or "dummy" variable. A context variable can be specified as part of an RSE and is valid only for the duration of the statement containing the RSE. Use of a context variable is a form of EXPLICIT context specification. Context variables can be useful in other situations also. Earlier we mentioned that the target of a STORE or MODIFY operation is placed only on the update context stack, not the general context stack. As a result, fields from the record that is being updated cannot be used on the right hand side of an assignment. For example, suppose you want to use previously entered values more than once, as in the example below. The statement "FIELD3 = FIELD1 + FIELD2" will cause DATATRIEVE to complain that FIELD1 is undefined. This is because FIELD1 is found only on the update stack. DTR> STORE DOMANE USING CON> BEGIN CON> FIELD1 = *."Field1 value" CON> FIELD2 = *."Field2 value" CON> FIELD3 = FIELD1 + FIELD2 CON> END "FIELD1" is undefined or used out of context. If you happen to have another selected record which contains fields named FIELD1 and FIELD2, then FIELD3 will take its value from the FIELD1 and FIELD2 values of the selected record rather than the record being stored, producing different results than you expected. You can get around these problems by using a context variable. In the following example, using the context variable Y has the effect of establishing an entry for DOMANE on the general context stack. This allows the values just entered for FIELD1 and FIELD2 to be used as the source of an assignment statement. DTR> STORE Y IN DOMANE USING CON> BEGIN CON> FIELD1 = *."Field1 value" CON> FIELD2 = *."Field2 value" CON> FIELD3 = Y.FIELD1 + Y.FIELD2 CON> END Context variables can also be used to avoid an exhaustive search on certain CROSS lookups. In the example below, BUILDER is a key for both OWNERS and YACHTS. If no context variable is used, a keyed lookup will be done on BUILDER from OWNERS. The same is true if the context variable for the second source, OWNERS (Z), is used in the RSE. If the context variable for the second source, YACHTS (Y), is used, then a keyed lookup will be done on both OWNERS and BUILDERS. The moral of this example is: when in doubt, use a context variable! DTR> FIND Y IN YACHTS CROSS Z IN OWNERS OVER BUILDER WITH BUILDER = "ALBIN" Performing EQL boolean on RMS key field Z.OWNERS.BUILDER DTR> FIND Y IN YACHTS CROSS Z IN OWNERS OVER BUILDER WITH Z.BUILDER = "ALBIN" Performing EQL boolean on RMS key field Z.OWNERS.BUILDER DTR> FIND Y IN YACHTS CROSS Z IN OWNERS OVER BUILDER WITH Y.BUILDER = "ALBIN" Performing EQL boolean on RMS key field Y.YACHTS.MANUFACTURER Performing EQL boolean on RMS key field Z.OWNERS.BUILDER 3.3 CONTEXT AND HIERARCHICAL FIELDS Hierarchical fields or lists are defined with an OCCURS clause in a record or a view. As mentioned earlier, context resolution becomes more complex when you use list records. Besides identifying a target record, you must identify a particular list occurrence. Unless otherwise stated, throughout the rest of this section "LIST" means any hierarchical field, including those defined in views. 3.3.1 General Techniques You can think of a list as a "pseudo-domain" within a domain and list items as records within that pseudo-domain. In the diagram below, the outer box represents the FAMILIES domain, and the inner box represents the pseudo-domain formed by the KIDS list within the FAMILIES domain. --------------------------------------------------------------- | 01 FAMILY. | | : | | 03 NUMBER_KIDS PIC 99. | | --------------------------------------------------------| | | | 03 KIDS OCCURS 0 TO 10 TIMES . . . | | | | 06 EACH_KID. | | | | 09 KID_NAME PIC X(10). | | . . . | | 09 AGE PIC 99. | | | | | | | --------------------------------------------------------- | --------------------------------------------------------------- You must supply context for the pseudo-domain as well as the actual domain. Some of the techniques to aid in target record identification include: oo Use successive FIND and SELECT statements oo Use nested FOR statements oo Use the CROSS clause to "flatten" the hierarchy oo Use an inner print list specification oo Use OF RSE clauses where possible oo Use the Context Searcher (SET SEARCH) Note that several of these techniques are also used for target record identification. This is because the techniques that are used on domains can also be applied to pseudo-domains. 3.3.1.1 Using FIND/SELECT One method of getting at records that are in the pseudo-domain is to use successive FIND and SELECT statements. First, lets look at an example of how you might naturally phrase a query on a list field. First you find and select a record from FAMILIES and then you try to print out the fields KID_NAME and AGE. The example below shows that the PRINT statement will fail because KID_NAME is undefined. DTR> FIND FAMILIES;SELECT DTR> PRINT ALL KID_NAME, AGE "KID_NAME" is undefined or used out of context Extra context must be provided to specify which item from the KIDS list is to be printed. Use an extra FIND to form a collection of "pseudo-records" from the KIDS pseudo-domain. DTR> FIND FAMILIES; SELECT DTR> FIND KIDS DTR> PRINT ALL KID_NAME, AGE KID NAME AGE URSULA 7 RALPH 3 3.3.1.2 Nested FORs The following is an example of an unsuccessful attempt to print items from the KIDS list using a FOR loop. The statement fails because it does not specify which occurrence of the list to print. DTR> FOR FAMILIES CON> PRINT KID_NAME, AGE "KID_NAME" is undefined or used out of context. In much the same way that you can access a pseudo-domain using an extra FIND, you can access the pseudo-domain using an extra FOR loop. In the following example the inner FOR loop provides a record stream from the pseudo-domain, providing context for KID_NAME and AGE. A list of kid names and ages will be printed. DTR> FOR FAMILIES CON> FOR KIDS CON> PRINT KID_NAME, AGE 3.3.1.3 Using CROSS The following is another example of a statement that does not provide context for the KIDS list. DTR> PRINT MOTHER, KID_NAME, AGE OF FIRST 1 FAMILIES "KID_NAME" is undefined or used out of context. The solution shown below is to use a CROSS clause to flatten the hierarchy. The CROSS provides a stream of records that contain fields from both the FAMILIES domain and the pseudo-domain providing context for KID_NAME and AGE. DTR> PRINT MOTHER, KID_NAME, AGE OF FIRST 1 FAMILIES CROSS KIDS MOTHER NAME AGE ANN URSULA 7 3.3.1.4 Inner Print Lists When accessing list fields through a PRINT statement, another option is available to you. That option is the use of the inner print list specification. The syntax of the inner print list is: {ALL print-list OF RSE} The print statement below does not provide context for the KIDS list. DTR> PRINT MOTHER, KID_NAME, AGE OF FIRST 1 FAMILIES "KID_NAME" is undefined or used out of context. The inner print list construct provides a way to specify context for a list field. Think of an inner print list as a print statement within a print statement which is used to access a pseudo-domain. The example below shows the inner print list portion of a print statement. --------------------------------- | print-list | | -------------------------| | | Inner print list || | | ------------- || | | | print-list| || | | | | || DTR> PRINT MOTHER, ALL KID_NAME, AGE OF KIDS OF FIRST 1 FAMILIES KID MOTHER NAME AGE ANN URSULA 7 RALPH 3 This method is most useful when you want to print all of the occurrences of a list. Note the difference in results from the previous CROSS example where only the first KID was retrieved. When using inner print lists keep in mind that if the print list starts with an inner print list, you need an additional "ALL" keyword: ALL {ALL print-list OF RSE} The following print statement is the same as the one in the previous example, but without the MOTHER print item. It produces a syntax error. DTR> PRINT ALL KID_NAME, AGE OF KIDS OF FIRST 1 FAMILIES Expected end of statement, encountered "OF". The print statement below contains an additional ALL to properly denote the beginning of the inner print list. ------------------------- | Inner print list | | ------------- | | | print-list| | | | | | DTR> PRINT ALL ALL KID_NAME, AGE OF KIDS OF FIRST 1 FAMILIES KID NAME AGE URSULA 7 RALPH 3 It is possible to define a list field within another list field. This is known as a nested list. The definition below shows the list PET defined within the list KIDS. ______________________________________________________________ | 01 FAMILY. | | 03 PARENTS. | | 06 FATHER PIC X(10). | | 06 MOTHER PIC X(10). | | 03 NUMBER_KIDS PIC 99. | | --------------------------------------------------------- | | |03 KIDS OCCURS 0 TO 10 TIMES DEPENDING ON NUMBER_KIDS. | | | | 06 EACH_KID. | | | | 09 KID_NAME PIC X(10). | | . . . | | 09 KID_AGE PIC 99. | | | | ----------------------------------------------- | | | | |09 PET OCCURS 2 TIMES. | | | | | | 13 PET_NAME PIC X(10). | | | | | | 13 PET_AGE PIC 99. | | | | | ----------------------------------------------- | | | --------------------------------------------------------| | ________________________________________________________________ Such a record requires yet another level of context resolution. The example below shows that an additional inner print list can be used to resolve the context for the list within the list. ---------------------------------------------- | print-list for selected record | | -------------------------------------|| | | inner-list for KIDS || | | -------------------- || | | |inner-list for PET| || | | | | || DTR> PRINT FATHER, ALL KID_NAME, ALL PET_NAME OF PET OF KIDS KID PET FATHER NAME NAME JIM GARY POP SODA SUE MOUSE SHORTY 3.3.1.5 OF RSE Clauses There are cases when you want to find a record with a list item of a certain value, but you do not care which occurrence of the list meets your qualifications. In other words, you want to find the records in which any of the list items satisfy an RSE. If you wanted to find all FAMILIES with one or more children over five years old, you might use the following FIND statement. The statement will fail, because even though you do not care which KIDS item meets your qualifications, DATATRIEVE needs to know how to resolve the reference to AGE. DTR> FIND FAMILIES WITH AGE GE 5 "AGE" is undefined or used out of context. You cannot use inner print lists on anything but a PRINT statement, so what can you do? You can use successive FIND/SELECT statements, but another possibility is to use an ANY boolean. Since the ANY boolean includes an "OF RSE" clause, it allows you to get through that extra pseudo-domain of KIDS as shown in the following FIND statement. Boolean expression ---------------------- | "inner" RSE | | ------------------ | | | DTR> FIND FAMILIES WITH ANY KIDS WITH AGE GE 5 [10 records found] 3.3.2 Report Writer Techniques Referencing list fields in the Report Writer is more of a challenge because only a few of the techniques discussed in the previous section can be used from within the report writer. To access lists from within the report writer, you can use: oo An inner print list specification oo The Context Searcher (SET SEARCH) oo CROSS to flatten the hierarchy The example below shows the inner print list technique being used inside of the report writer. A couple of points to note are: oo The inner print list is made up of KID_NAME and AGE as well as a column specification. oo Use of the TOTAL function on the field AGE requires the OF RSE specification, but TOTAL of NUMBER_KIDS does not. This is because AGE is a list field and needs additional context. DTR> REPORT FAMILIES RW> PRINT COL 1, MOTHER, COL 20, ALL KID_NAME, COL 40, RW> AGE OF KIDS RW> AT BOTTOM OF MOTHER PRINT TOTAL NUMBER_KIDS, TOTAL AGE OF KIDS RW> END_REPORT 3.4 The CONTEXT SEARCHER Another method of providing the extra context needed for list fields is the use of the CONTEXT SEARCHER. In addition, the CONTEXT SEARCHER can provide context for access through DBMS sets. The CONTEXT SEARCHER is activated by the SET SEARCH command. The following example shows the CONTEXT SEARCHER being used to automatically create an inner print list for KIDS. The print statement below is the same one that failed in a previous example. Note that DATATRIEVE displays an informative message when context is resolved by the CONTEXT SEARCHER. DTR> SET SEARCH DTR> PRINT MOTHER, KID_NAME, AGE OF FIRST 1 FAMILIES Not enough context. Some field names resolved by Context Searcher. KID MOTHER NAME AGE ANN URSULA 7 RALPH 3 The CONTEXT SEARCHER also allows you to search DBMS records without specifying set relationships explicitly. Consider the following DBMS record/set structure: ------------------ | DIVISIONS | ------------------ | /\ | | CONSISTS_OF MANAGES | | \/ | ------------------ | EMPLOYEE | ------------------ The following example prints the division name associated with the first employee. An EMPLOYEE record is selected and then the print statement accesses the associated DIVISION record through the CONSISTS_OF set. DTR> FIND FIRST 1 EMPLOYEE [1 record found] DTR> SELECT DTR> PRINT DIV_NAME OF DIVISION OWNER OF CONSISTS_OF Division Name------- ENG BUILD ~& TEST In the following example the CONTEXT SEARCHER is used, so set access does not need to be specified in the print statement. Note that in this example, the Context Searcher traverses the MANAGES set instead of the CONSISTS_OF set. This is because the Context Searcher may use any of the paths available through a set structure. You should exercise caution when using the CONTEXT SEARCHER with DBMS because it may not choose the expected path. DTR> SET SEARCH DTR> FIND FIRST 1 EMPLOYEE [1 record found] DTR> SELECT DTR> PRINT DIV_NAME Not enough context. Some field names resolved by Context Searcher. Division Name------- ENG STOCKROOM 4 COMMON MISTAKES AND PITFALLS When you see the following message, it means that DATATRIEVE does not have enough context to do what you want it to. " <...> is undefined or used out of context" There are a number of possible causes for context problems. Here is a check list to consider: oo Check for misspellings oo Check for duplicate names, especially from other sources oo Does a referenced collection have a SELECTed record? Check the CURRENT as well as named collections. oo Is a list involved (either from OCCURS or views)? Especially in the Report Writer, you may need to use a CROSS to flatten lists, use inner print lists or the CONTEXT SEARCHER. oo Does a STORE operation need to use a context variable in order to reference just-stored values? oo For DBMS sources, does a set name need to be referenced? oo Is there a FIND inside a compound statement? This can sometimes cause unexpected results rather than an error message. In the following example, a YACHTS record will be printed even though a FIND OWNERS/SELECT was done just before the PRINT statement. READY YACHTS, OWNERS FIND ALL YACHTS BEGIN FIND OWNERS ! Not supported SELECT ! Not supported PRINT END Note: A YACHTS record is printed! oo Some query-names from records defined with RDO and CDDL are not handled correctly by DATATRIEVE. Those are CDDL records with query names containing a hyphen and RDO records containing a hyphen or lower-case letters. DATATRIEVE does not convert the hyphens and lower case letters as it should, resulting in an undefined field name. This problem will be corrected in a version of DATATRIEVE following version 4.1. The example below illustrates this problem. CDDL: define record qph. tmpr structure. date1 datatype is date query_name for dtr is "quad-one". end tmpr structure. end qph record. DTR> show fields QPHD TMPR DATE1 (quad-one) DTR> find qphd DTR> print all quad-one "QUAD_ONE" is undefined or used out of context. oo Check that all variables referenced in a procedure are declared. Do not assume that a variable referenced in code which is branched around with a CHOICE or IF-THEN-ELSE statement does not need to be declared. Even if that code is never executed, it still must be compiled. For example: DTR> X = 0 DTR> IF X = 1 THEN PRINT x + z "Z" is undefined or used out of context. oo Context errors do not always result in an error message! Forgetting about implicit context can also cause unexpected data results. You would expect the example below to produce a context error message because context is not provided for KIDS. But, it turns out that there is a selected record with URSULA targeted as the KID. Remember that the general context stack also includes collections with selected records! DTR> SET NO SEARCH DTR> FOR FAMILIES PRINT KID_NAME, AGE KID NAME AGE URSULA 7 URSULA 7 URSULA 7 : (etc.) oo A CROSS of two collections from the same domain may give incorrect answers. To avoid this problem ready the domain twice, once with an alias and once without, and proceed as if you were dealing with two separate domains. For example: DTR> READY DOM, DOM AS EXTRA DTR> FIND COLLECTION-A IN DOM DTR> FIND COLLECTION-B IN EXTRA DTR> FIND A IN COLLECTION-A CROSS B IN COLLECTION-B 5 Summary The outcome of a DATATRIEVE statement is affected by the context in which it executes. This makes DATATRIEVE a more powerful tool for you, because you do not have to supply every detail of execution on a DATATRIEVE statement. Context resolution in DATATRIEVE can sometimes be confusing. If you keep in mind how DATATRIEVE handles context, as described in this paper, you can avoid confusion and make context work for you. _______________________________________________________________________________ The Story of 'P' Mr. R, Oldest Seagoing Service, Washington, DC 20593 _______________________________________________________________________________ I would like to call this article, "The Story of 'O'" but the old time sailors where I work tell me that letter has been taken. So I am going to call this "The Story of 'P'". I am a system manager/programmer/comic relief for the oldest sea-going service in the United States. My boss has a secretary that works for him as well as handling overflow tasks for other division chiefs. I'll call that secretary "P." in order to protect the innocent (and the guilty). There was a problem in the office that we could never find "P." when we needed her. She would appear to play her boss against the other people that gave her work to do. She would also take in-ordinate amounts of times to perform simple tasks. After much coun-selling with no long term improvement my boss said something had to be done. That's where I come in. I looked at what information my boss needed and saw three items, the time 'P.' left her desk, where 'P.' went, and when 'P.' returned to her desk. The record defini- tion I came up with looked like this: DEFINE RECORD LOCATION_REC USING 01 LOCATION_RECORD. 03 TIME_OUT USAGE DATE EDIT_STRING IS X(20). 03 TIME_IN USAGE DATE EDIT_STRING IS X(20). 03 DESTIN PIC X(70). ; I then went ahead and defined the domain LOCATION like this: DEFINE DOMAIN LOCATION USING CDD$TOP.APA.P.LOCATION_REC ON USER1:[APA.P]LOCATION.DAT; Next I defined a file using something like the command: DEFINE FILE FOR LOCATION KEY = TIME-OUT ; Now I wanted to come up with two very simple procedures for 'P.' to use, one to say when she was leaving and one to use when she was back. I wanted the procedures to be "foolproof" as possible so I needed to make sure "P." could not be out to someplace without having returned from her last "adventure" and that the time was recorded automatically. Here is what I came up with: DEFINE PROCEDURE LEAVE READY LOCATION SHARED WRITE IF COUNT OF LOCATION WITH TIME_IN = "" GT 0 THEN BEGIN PRINT " " PRINT " You must first log in from your last adventure !" PRINT " Please type: BACK " PRINT " " END ELSE BEGIN STORE LOCATION USING BEGIN TIME_OUT = "NOW" DESTIN = *."your destination" END END END_PROCEDURE Now for when 'P.' returned, I needed to make sure she had first recorded her leaving and that time was recorded automatically. Here is what I came up with: DEFINE PROCEDURE BACK DECLARE TIC USAGE DATE EDIT_STRING X(20). TIC = "now" READY LOCATION SHARED MODIFY IF COUNT OF LOCATION WITH TIME_IN = "" EQ 0 THEN BEGIN PRINT " " PRINT " You must first leave on an adventure !" PRINT " Please type: LEAVE " PRINT " " END ELSE BEGIN MODIFY LOCATION WITH TIME_IN = "" USING TIME_IN = "NOW" PRINT " Logged in. Time is " | (FORMAT(TIC) USING X(20)) END END_PROCEDURE Now my boss needed some way to to tell where 'P.' was. The hard part of this was deciding what to call it. Since I think my boss had spent some time in Matamoras, Mexico, we decided to call it "DONDE" which is Spanish for "Where". Here's what I came up with: DEFINE PROCEDURE DONDE SET DICTIONARY CDD$TOP.APA.P READY LOCATION SHARED IF COUNT OF LOCATION WITH TIME_IN = "" EQ 0 THEN BEGIN PRINT " Not logged out anywhere " END ELSE BEGIN FOR LOCATION WITH TIME_IN = "" PRINT TIME_OUT(-), SKIP, DESTIN(-) END END_PROCEDURE I then set up symbols in LOGIN.COM for 'P.' and my boss. They looked like: BACK :== "''DTR32' ; :CDD$TOP.APA.P.BACK" LEAVE:== "''DTR32' ; :CDD$TOP.APA.P.LEAVE" DONDE:== "''DTR32' ; :CDD$TOP.APA.P.DONDE" Datatrieve was defined as a symbol elsewhere like: DTR*32 :== $SYS$SYSTEM:DTR32411 This went pretty good for the first day but then my boss said he wanted to know how long each trip lasted. I told him, "I got five records out there and you are not going to destroy all my work by creating a new data file." He replied, "How are you going to do it then ?" I said, "Well, you could use that old favorite of Cobol programmers, Mr. REDEFINES. You and I both know that a Datatrieve date field uses the same amount of space as a quad field. So you take the date, convert it to a quad, subtract the two dates as quads, and convert the resulting value back to minutes." "A value of '1' in a quad field is also known as a clunk. But how many clunks are there in a minute ?" "That's trivia and I don't know know. But I do know that 17-NOV-1858 at 00:00:00.0 is equal to '0' clunks since time for all Datatrievers starts at that date. I would suspect that if you take the date 18-NOV 1858 at 00:00:00.0 you could put that in a variable with usage quad and see what it is in clunks and then divide by twenty-four to get the number of hours and again by sixty to get the number of minutes." "And you tell me that 17-NOV-1858 at 00:00:00.0 is not trivia ?" So what I guess he did was something like this: DTR> declare TIC usage date. DTR> declare TIC usage date. DTR> declare TOC usage quad. DTR> TIC = "18-NOV-1858 00:00:00.00" DTR> TOC = TIC DTR> TOC = TOC / 24 DTR> TOC = TOC / 60 DTR> print TOC(-) 600000000 DTR> Because what I ended up with was: REDEFINE RECORD LOCATION_REC USING 01 LOCATION_RECORD. 03 TIME_OUT USAGE DATE EDIT_STRING IS X(20). 03 TIME_OUT_RED REDEFINES TIME_OUT. 05 TIME_OUT_QUAD USAGE IS QUAD. 03 TIME_IN USAGE DATE EDIT_STRING IS X(20). 03 TIME_IN_RED REDEFINES TIME_IN. 05 TIME_IN_QUAD USAGE IS QUAD. 03 DESTIN PIC X(70). 03 TOT_TIME COMPUTED BY (TIME_IN_QUAD - TIME_OUT_QUAD)/ (600000000). The COMPUTED BY field works quite nicely at figuring up the time away. The reports we are now getting are too hard to believe and we have decided not to share them with you. (Would you believe 'P.' was at the supply locker for 187 minutes? I thought not!) We now are having a better time keeping track of 'P.' with the use of Datatrieve. I am happy, my boss is happy, and everyone except 'P.' is happy. _______________________________________________________________________________ Documenting and Manipulating SYSGEN Parameters using Datatrieve B. Z. Lederman, WU World Communications, New York, NY 10004-2464 _______________________________________________________________________________ As a system manager, I have to keep track of the SYSGEN parameters on my system. I also have to keep such files as MODPARAMS.DAT up to date, and with various changes in VMS and layered products add the proper changes. Part of this process can be automated using Datatrieve. One of the things any system manager should do is save a copy of the SYSGEN parameters. This is done as follows: $ SET DEFAULT SYS$SYSTEM $ MC SYSGEN SYSGEN> USE CURRENT SYSGEN> SET /OUTPUT = PARAMS.28JAN88 SYSGEN> SHOW /ALL SYSGEN> EXIT The output file can of course be named as you choose: I like to put on the date for reference. Also, you should occasionally make such a record doing a "SHOW /SPECIAL" in addition to "SHOW /ALL": but what I am doing here is generating a change list, and since special parameters normally should not be changed I'm not including them here. A small section of this file looks like this: Parameters in use: Active Parameter Name Current Default Minimum Maximum Unit Dynamic -------------- ------- ------- ------- ------- ---- ------- PFCDEFAULT 64 32 0 127 Pages D KFILSTCNT 16 4 2 255 Slots GBLSECTIONS 400 128 20 4095 Sections GBLPAGES 20500 4096 512 -1 Pages GBLPAGFIL 2048 1024 128 -1 Pages MAXPROCESSCNT 128 72 12 8192 Processes PROCSECTCNT 32 32 5 1024 Sections MINWSCNT 20 20 10 -1 Pages PAGFILCNT 2 2 1 63 Files .... The hard part comes in looking through all of the parameters on this listing and finding the ones that are not at default values. This is where Datatrieve makes things easier. A domain can be defined to read this text file: REDEFINE RECORD TPARAMS_RECORD 01 TPARAMS_REC. ! ! Read in a "Raw" text file output of parameters from SYSGEN ! ! B. Z. Lederman ! 10 NAME PIC X(16). 10 CURRENT PIC X(18). 10 SUBCUR REDEFINES CURRENT. 20 FILLER PIC X(8). 20 RCUR PIC X(10). 10 DEFAULT PIC X(10). 10 MINIMUM PIC X(10). 10 MAXIMUM PIC X(10). 10 FILLER PIC X. 10 UNIT PIC X(12). 10 DYNAMIC PIC X. ; REDEFINE DOMAIN TPARAMS USING TPARAMS_RECORD ON SYS$SYSTEM:PARAMS.28JAN88; The domain definition will have to be re-defined each time the file name changes, or you can use a logical name to point to the current file. The above definition will allow you to read in the text as text: but a lot more can be done if the numeric parameters could be manipulated as numbers. To do this, I define a matching domain with numeric fields: REDEFINE RECORD PARAMS_RECORD ! ! Write out "normalized" SYSGEN parameters. ! ! B. Z. Lederman ! 01 PARAMS_REC. 10 NAME PIC X(16). 10 CURRENT USAGE LONG EDIT_STRING ZZZ,ZZZ,ZZ9. 10 DEFAULT USAGE LONG EDIT_STRING ZZZ,ZZZ,ZZ9. 10 MINIMUM USAGE LONG EDIT_STRING ZZZ,ZZZ,ZZ9. 10 MAXIMUM USAGE LONG EDIT_STRING ZZZ,ZZZ,ZZ9. 10 UNIT PIC X(12). 10 DYNAMIC PIC X. ; REDEFINE DOMAIN PARAMS USING PARAMS_RECORD ON PARAMS.SEQ; Now all I have to do is move the data from one domain to the other. Since the input is in a text file with a title, I want to elimiate the title lines: I also don't want to move over any parameters which are text rather than numbers (such as QUORUM_DISK and SCSNODENAME): REDEFINE PROCEDURE CONVERT_PARAMETERS ! ! Convert SYSGEN parameters from text to "normalized" numbers. ! TPARAMS must first be defined to read the correct file. ! Only numeric parameters are converted. ! ! B. Z. Lederman ! READY TPARAMS DEFINE FILE FOR PARAMS; ! remember to purge old files READY PARAMS WRITE ! FOR TPARAMS WITH UNIT NE "Ascii", "Unit Dynami", " " STORE PARAMS USING PARAMS_RECORD = TPARAMS_RECORD END_PROCEDURE Note that there are two blank spaces between "Unit" and "Dynami". Now, I can manipulate this record of my SYSGEN parameters as I please. For example, If I had multiple machines with different parameters I could define a separate domain for each machine and compare the two to see where they differ. What I am most interested in now, however, is to generate a list of commands for MODPARAMS.DAT which will restore my current system settings from the VMS default settings. REDEFINE PROCEDURE PRINT_PARAMETERS ! ! Print out the normalized parameters in "ADD_" format. ! Values are current minus default. ! ! B. Z. Lederman ! FOR PARAMS WITH CURRENT NE DEFAULT SORTED BY NAME PRINT "ADD_" | NAME ||| "=" ||| (CURRENT - DEFAULT) ON *."TT or filespec" END_PROCEDURE The portion of the output from this procedure looks like this: ADD_ACP_DINDXCACHE = 24 ADD_ACP_DIRCACHE = 118 ADD_ACP_HDRCACHE = 70 ADD_ACP_MAPCACHE = 16 ADD_ACP_QUOCACHE = -64 ADD_BALSETCNT = 63 ADD_BORROWLIM = 518 ADD_DEADLOCK_WAIT = -8 ADD_FREEGOAL = 455 ADD_FREELIM = 87 ADD_GBLPAGES = 16404 ADD_GBLPAGFIL = 1024 ADD_GBLSECTIONS = 272 and so on. While this file is not completely ready to use as it is (the ASCII parameters must be re-inserted, and some parameters may be better off as absolutes as, for example, ACP_QUOCACHE which I am setting to zero as we don't use quotas), it is certainly very much easier to have Datatrieve search all of the parameters and find out which ones have changed than to have to do it all by hand. And, it saves a lot of typing to have Datatrieve output the changes in the form where they are ready to go into MODPARAMS.DAT. It would also be possible to move the ASCII parameters into a separate file, or make the record definition a little more elaborate and have them in the same file. But there aren't many of them, and I don't change system parameters very often, so I consider the process to be sufficiently useful in it's present form. ________________________________________________________________________________ Errata for Wombat Wizard's column, February 1988, Volume 3, Number 6 ________________________________________________________________________________ The second paragraph on page DTR-5 should read: ------------------------------------------------------------ A VAX Date is measured in units called "klunks." Each klunk is 100 nanoseconds long (0.1 microseconds), so there are 10,000,000 or 10**7 klunks in a second. The VAX date is a number of klunks since a base time, and is stored in the VAX as a quadword (four 16-bit words, or 64 bits). The VAX base time (the "zero" time) is the same as the base time for the Smithsonian Astrophysical time, midnight on November 17, 1858. The VAX Date for that time is a quadword with a value of zero. For each second since that time, the quadword is incremented by 10,000,000, and each day is represented by an additional number of klunks that can be computed by the formula klunks/day = 24 hours/day * 60 minutes/hour * 60 seconds/minute * 10,000,000 klunks/second = 864,000,000,000 ------------------------------------------------------------ The next to last paragraph on page DTR-6 should read: ------------------------------------------------------------ In this example, 10**7 klunks were added to a quadword, yielding a date (plus time) one second later. However, ... ------------------------------------------------------------ See you in Cincinatti!