←Part 24  Part 26→

The HP 3000--For Complete Novices
Part 25: IMAGE/SQL Application Programs--Error Handling

Feature by George Stachnik
In the previous article in this series, we began to look at the intrinsic interface to IMAGE/SQL. This time, we'll explore some of the IMAGE/SQL intrinsics in a bit more detail. In particular, we'll see how IMAGE/SQL application programs handle errors of various kinds.

We first covered the idea of intrinsics way back in Part 17 of this series, in an article entitled "Using Intrinsics." Since it's been a while since we discussed intrinsics in this series, it's worth taking a few moments now to review what intrinsics are and why we use them in our application programs.

In HP 3000 terminology, all software is built from building blocks called procedures. A procedure is a piece of software that can be "called" by other procedures. The idea of callable procedures is a very common programming concept that is found on virtually all computer platforms. If you learned about programming on Microsoft Windows, on UNIX, or on some other operating system or platform, you may have learned different terminology, but the concept is the same on all of these. The terms routines, subroutines, and modules have all been used at one time or another to describe things that are conceptually identical to MPE's callable procedures.

An intrinsic is a special kind of procedure. MPE/iX is made up of hundreds of procedures, most of which cannot be called by user programs, at least not directly. The intrinsics are unique in that they can be called by ordinary user programs. Each intrinsic is designed to do a specific job. Earlier in this series, we examined some of the intrinsics that can be used to open files (FOPEN and HPFOPEN). In this article, we're going to look at a few of the intrinsics that you'd use in order to access an IMAGE/SQL database on an HP 3000. In particular, we're going to see how your application program should handle errors that it may encounter along the way.

We saw last time that DBOPEN is used to open IMAGE/SQL databases. When you call DBOPEN, you must tell it the name of the database you want to open by passing it to the procedure as a parameter. A parameter is a data item that is defined by the calling procedure and made available to the called intrinsic. When an application program calls an intrinsic, it uses the same mechanism that is used by programmers to call user-written procedures (subroutines). This is true in virtually every third-generation programming language (COBOL, Fortran, C, etc.). Since most HP 3000 applications were written in COBOL, we'll use COBOL examples to show how IMAGE/SQL intrinsics are typically used.

In the COBOL language, intrinsics can be invoked using the industry standard CALL verb. Figure 1 shows some sample COBOL code that calls DBOPEN. The statement that actually passes control from the user-written COBOL procedure to the DBOPEN system procedure is the "CALL INTRINSIC" statement that appears in lines 26600 to 26900.

Figure 1:

DBOPEN is a general-purpose tool for opening databases. It doesn't "know" what database you want to open, or how. Therefore, when you call DBOPEN, you must give it this information. Intrinsics such as DBOPEN expect to have this information passed to them via parameters. In a COBOL program, a parameter is a data item in the calling procedure that is made available to the called intrinsic or procedure.

In the example shown in Figure 1, we're passing DBOPEN four parameters:
  • DB-NAME: This data item contains the name of the database you are opening.
  • DB-PASSWORD: This is the database password (as defined in the schema).
  • DB-MODE: The mode in which we want the database opened.
  • STATUS-ARRAY: A status array.
Whenever you call DBOPEN, you must always pass it the four parameters defined above, and always in the order shown in Figure 1. DBOPEN always expects to see these four parameters in this order. Calling DBOPEN without a correct parameter list will generate an error.

Let's look at each of these parameters individually. The top parts of Figure 1 contain sample COBOL code from the DATA DIVISION of our program. Parameters are defined in the DATA DIVISION. The first parameter, DB-NAME, is defined in line 001930 as "X(10) VALUE SPACES." This means that 10 bytes have been reserved for DB-NAME, and initialized to ASCII space characters.

Returning to the lower portion of Figure 1, line 25800 moves the string " ORDERS;" to DB-NAME. Note the semicolon at the end of the database name. DBOPEN always expects the string containing the database name to be terminated with a semicolon. When DBOPEN is called in the way shown in Figure 1, it will attempt to open a database named ORDERS. If no such database exists, an error will be generated.

The second parameter passed to DBOPEN contains the database password. Earlier in this series, we saw how database passwords are defined in the schema. Figure 2 contains a review of this material. In the partial schema shown in Figure 2 each password is associated with a class number. When you open an IMAGE/SQL database, you must specify one of the database passwords that was defined in the schema. The password that you specify determines your class number, based on the rules defined in the schema. Your assigned class number, in turn, determines which parts of the database you can access and how. For example, if we open the database using a password of CLERK, IMAGE/SQL will assign us a class number of 4.

Figure 2:

Each data item and each dataset in the database is associated with a list of class numbers in the schema. The READ numbers appear first, followed by the WRITE numbers. The READ list and the WRITE lists are separated by a forward-slash ("/"). In Figure 2 the class number 4 appears in the READ list for the items ACCOUNT, CITY, and ZIPCODE and for the CUSTOMER dataset. So, if we open the database with a password of CLERK, we have READ access to all these items and datasets. The sample COBOL code shown in Figure 1 is passing the string "CLERK;" to DBOPEN in the password parameter. Like the database name, the password must be terminated with a semicolon.

Note that the passwords specified in the schema can be hard coded in application programs (as shown in Figure 1), or entered by the user in response to a prompt (as we saw last month when we opened a database using the QUERY program).

Returning to the call to DBOPEN shown in Figure 1, the third parameter passed to the intrinsic is DB-MODE. This mode parameter is a 16-bit binary number. In the sample code shown in the figure, it is defined in statement 001920 as "S9(04) COMP," and initialized in statement 026000 to a value of 3. We discussed the database open mode in Part 24 of this series, and Table 1 shows the values that are recognized by IMAGE/SQL. A mode of 3 specifies that we are asking for exclusive access to the database in question, and that we want to be able to modify the database.

By hard coding this value into our program, we are effectively saying that this program will execute correctly only if it can get exclusive access to the ORDERS database. When our program calls DBOPEN, it will attempt to open ORDERS in mode 3. As long as nobody else is running a program that has ORDERS opened (and remember, this includes 4GLs and programs like QUERY), the call to DBOPEN will work fine.

What happens if something goes wrong? For example, suppose that somebody else does have the database open, making our request for exclusive (mode 3) access fail. That brings us to the fourth and final parameter that we are passing to DBOPEN: the STATUS-ARRAY. In the sample code shown in Figure 1, the STATUS ARRAY is defined in the middle box (from lines 002380 through 002440), and passed to DBOPEN as the fourth parameter (line 026900).

The STATUS-ARRAY is an array of six binary numbers. The code in Figure 1 assigns the following names to the six parts of the status array:
  • CONDITION-CODE
  • ENTRY-LENGTH
  • RECORD-NUMBER
  • CHAIN-LENGTH
  • BACKWARD-POINTER
  • FORWARD-POINTER
The status array will be used by every IMAGE/SQL intrinsic that we call. That is, whether you are calling DBOPEN, DBGET, DBPUT, DBCLOSE, or any of the other IMAGE/SQL intrinsics, you will always pass the STATUS-ARRAY as a parameter.

When a call to an IMAGE/SQL intrinsic succeeds, control is returned to the calling program, and a value of zero is placed in the first word of the status-array (in the sample code shown in Figure 1, this is the data item called CONDITION-CODE). If our call to DBOPEN were to fail for any reason, IMAGE/SQL would place a non-zero value in the condition-code item, and return control to the calling program. It is important for programmers to remember to check the value in CONDITION-CODE after every IMAGE/SQL intrinsic call and handle errors appropriately.

Figure 3 contains some sample COBOL code that shows how this might be done. A standard COBOL "IF" statement is used to test the value in CONDITION-CODE. This statement should always be the first thing that's executed after an intrinsic call. If the value found in CONDITION-CODE is anything other than zero, the code shown in Figure 3 will pass control to a user-written procedure, which in this case we've called IMAGE-ERROR. Otherwise, control falls through to the next statement, which in this case displays a message indicating that the ORDERS database has been successfully opened.

Figure 3:

The IMAGE-ERROR routine shown in Figure 3 calls another IMAGE/SQL intrinsic called DBEXPLAIN. DBEXPLAIN is a general-purpose error-handling intrinsic whose purpose is to analyze errors that might be reported back by any of the other IMAGE/SQL intrinsics. It contains a complete and up-to-date table of possible error messages. When you call DBEXPLAIN, you must pass it one parameter only: the status array containing the non-zero condition code that was returned by one of the other IMAGE/SQL intrinsics. DBEXPLAIN will analyze the error, and display an appropriate error message on $STDLIST.

When DBEXPLAIN is finished, it will return control to the calling application program so that any additional error handling logic can be performed at that time. In the example shown in Figure 3, there is no additional error handling shown; we simply execute the COBOL "STOP RUN." verb, which terminates execution of this program. Usually, a failure to open a database successfully is a catastrophic error--there's not much else that a program can do if it cannot open the database. Hence, the error handling routine shown in Figure 3 takes the drastic action of terminating the program.

Once a database has been successfully opened, our application program is free to begin accessing the data stored in the database. Here, too, the path is fraught with possible errors, and this time around the situation is a bit more complicated. When DBOPEN failed, there wasn't much for us to do but to terminate the program. But once we begin accessing the database, many of the errors we might encounter are less drastic. In many cases, our COBOL program can be perfectly capable of handling the error and continuing. Figure 4 shows an example of how a COBOL program might handle some less catastrophic errors.

Figure 4:

Beginning at line 052500, the sample code shown in Figure 4 prompts the user to enter an order number. The value that the user types is placed in a data item called SEARCH-ITEM-VALUE. Next, we call the DBFIND intrinsic. It's important to understand that DBFIND will not retrieve any records from the database. (That job is handled by DBGET, as we'll see in just a moment.) DBFIND's job is simply to determine whether there are any records in the database that meet a specified criterion. In the last article, we learned that a collection of records in a detail dataset that share a common search-item value is referred to as a chain. The purpose of DBFIND is to locate chains of data entries.

The code shown in Figure 4 will locate a chain of records in the dataset called ORDER-DETAIL. We are looking for a chain of data entries in which the data item called ORDER-NO contains a common value (specifically, the value that the user typed, which is now to be found in SEARCH-ITEM-VALUE). The call to DBFIND is located in line 053700, and the error handling logic begins in line 054500.

In line 054500, we test the condition code returned by DBFIND. If it is zero, then all is well. Control will fall through to line 055300, where we will retrieve the first record in the chain. But if the condition code is not zero, we do not want to call IMAGE-ERROR immediately as we did in Figure 3. Instead, we first test CONDITION-CODE for a value of 17 (this test appears in Figure 4 in line 054600).

A value of 17 in the condition code is IMAGE/SQL's way of telling us that there are no records that meet the criteria we passed to DBFIND. This is hardly a catastrophic error; it probably just means that the user mistyped something. So instead of calling IMAGE-ERROR and terminating the program, we simply display an appropriate error message in line 054700. At this point, the program should loop back and prompt the user for another value (this logic is not shown in the figure).

If, on the other hand, the CONDITION-CODE value returned by DBFIND is anything other than 17, then this programmer has assumed that whatever has gone wrong, it's nothing that he wants to try to handle himself. Since most of the other errors that DBFIND might return concern corruption in the database, this is not a bad assumption to make. So the code shown in Figure 4 simply performs the IMAGE-ERROR routine (line 054900), which, as we saw in Figure 3, will call DBEXPLAIN and terminate the program.

The remainder of the code shown in Figure 4 is used to read the first record in the chain that was found by DBFIND.

In looking through these examples, you can begin to get a feel for the kind of logic frequently found in IMAGE/SQL application programs. Next time, we'll explore that logic in greater detail.


George Stachnik works in technical training in HP's Network Server Division.
  ←Part 24  Part 26→
     [3khat]3kRanger   [3khat]3kMail   Updated