*DBMS_RECORD_LOCKED must be used only if there was an I/O error

Date:Archived
Product/Release:LANSA for the AS/400
Abstract:*DBMS_RECORD_LOCKED must be used only if there was an I/O error
Submitted By:LANSA Technical Support

The *DBMS_RECORD_LOCKED variable is used to detect if the previous I/O operation (e.g. SELECT or FETCH) failed because the record was locked during the allowable wait time (i.e. "Maximum record wait time" file parameter, usually 60 seconds). This variable is mapped from an RPG field (error code) that is updated only when there is an I/O error. Therefore, use this variable only within the scope of the I/O error logic (e.g. IF COND(#IO$STS *NE OK). Following is an extract from the LANSA Technical Guide ("4.7 I/O Return Codes" section, under the "4.0 Common Command Parameters and Functions"):

4.7.2 I/O Status Record Locked

In addition to the return codes previously mentioned the system variable *DBMS_RECORD_LOCKED can be used to distinguish between an I/O error status of record locked and other I/O errors.

The following example shows how the IO_ERROR parameter passes control to the label TST when an error occurs and the GOTO NXT by-passes the if condition when the command was successful.

UPDATE FIELDS(#ORDERQTY) IN_FILE(ORDLINE)
WITH_KEY(#ORDER #LINE) IO_ERROR(TST)
GOTO LABEL(NXT)
TST IF COND('*DBMS_RECORD_LOCKED *EQ Y')
MESSAGE MSGTXT('Order line record locked')
 ........ ..Required action
ELSE
ABORT MSGTXT('Fatal I/O error on ORDERLINE file')
ENDIF
NXT ....... ..Next action

The first paragraph of the extract clearly states that the *DBMS_RECORD_LOCKED condition is an I/O error. It also says that it can be used "In addition to the return codes". Perhaps, this paragraph should have been more explicit in saying only to use the *DBMS_RECORD_LOCKED variable, if an I/O error occurs. Also, check that the *DBMS_RECORD_LOCKED variable is checked only if there was an I/O error.

The following sample code shows the potential problems of using the *DBMS_RECORD_LOCKED without checking the I/O error:

FUNCTION OPTIONS(*DIRECT)
GROUP_BY NAME(#HEADER) FIELDS((#DEPTMENT))
GROUP_BY NAME(#DISPLAY) FIELDS((#DEPTMENT) (#DEPTDESC))
BEGIN_LOOP
MESSAGE MSGTXT('Enter a Department code to lock')
L1 REQUEST FIELDS((#HEADER)) EXIT_KEY(*NO) MENU_KEY(*YES END)
PROMPT_KEY(*NO)
FETCH FIELDS((#DISPLAY)) FROM_FILE(DEPTAB)
WITH_KEY(#DEPTMENT) IO_ERROR(*NEXT) ISSUE_MSG(*YES)LOCK(*YES)
IF COND(*DBMS_RECORD_LOCKED)
MESSAGE MSGTXT('Record is locked.')
GOTO LABEL(L1)
ENDIF
BEGIN_LOOP
MESSAGE MSGTXT('Record should be locked now')
DISPLAY FIELDS((#DISPLAY)) EXIT_KEY(*NO)
MENU_KEY(*YES SKP) PROMPT_KEY(*NO)
END_LOOP
SKP UPDATE FIELDS((#DISPLAY)) IN_FILE(DEPTAB)
IO_ERROR(*NEXT) ISSUE_MSG(*YES)
MESSAGE MSGTXT('Unlocking record now')
END_LOOP
END RETURN

Run the above on two Workstations simultaneously. After one session releases the lock, the other still have the *DBMS_RECORD_LOCKED variable set to Y, which should not have been used if the I/O Status was OK. The proper implementation of the above function should have the IF condition after the FETCH as follows:

IF COND('(#IO$STS *NE OK) AND (*DBMS_RECORD_LOCKED = Y)')
MESSAGE MSGTXT('There was an I/O Error. The record was locked.')
GOTO LABEL(L1)
ENDIF

In this way the *DBMS_RECORD_LOCKED is used in conjunction to the #IO$STS which is the right thing to do.