Try OpenEdge Now
skip to main content
Error Handling
Handling Errors with CATCH Blocks : Buffer scope within a CATCH block
 

Buffer scope within a CATCH block

In the previous section, the example showed that a record buffer scoped to the associated block of a CATCH will not be available in the CATCH block. The record buffer will be available if the ABL scoping rules have the buffer scoped to a block outside the associated block. If a buffer referenced in a CATCH block is referenced outside of the associated block, then the scope of that buffer is the smallest enclosing block outside of the associated block that encompasses all references to the buffer.
However, note that an available buffer in a CATCH block will be affected by the UNDO operation of the associated block if that buffer is part of the transaction in the associated block. In this case, the AVM restores the buffer to the last valid state of the buffer before the associated block began its execution.
This next example demonstrates buffer availability in the common use case of looping through associated records:
DEFINE VARIABLE i AS INTEGER NO-UNDO.

/* An outer loop for batch updates to Customer records */
FOR EACH Customer WHERE CustNum < 2:

/* Customer buffer scoped to FOR EACH. Will be available to inner CATCH. */
ASSIGN Customer.Name = Customer.Name + "_changed".

/* Update Order records for current Customer. */
DO i = 1 TO 5 ON ERROR UNDO, LEAVE:
IF i = 1 then
FIND FIRST Order OF Customer.
ELSE
FIND NEXT Order OF Customer.

/* Order record scoped to the DO block, which is the associated block. */
ASSIGN Order.PromiseDate = TODAY.

/* Nonsense code raises unique index error.
DO block undone.
Control passes to CATCH */
ASSIGN Customer.CustNum = 2.

CATCH eSysError AS Progress.Lang.SysError:
/* MESSAGE statement executes to display available Customer data.
Unavailable Order data raises two errors.
Errors thrown to FOR EACH block.
FOR EACH block undoes iteration and attempts NEXT iteration.
Since there is not another iteration, FOR EACH completes. */
MESSAGE Customer.Name SKIP
Order.OrderNum Order.PromiseDate
VIEW-AS ALERT-BOX BUTTONS OK.
END CATCH.
END. /* DO */
END. /* FOR EACH */

FIND Customer WHERE Customer.CustNum = 1.

/* Demonstrates change made in FOR EACH block is also undone. */
MESSAGE Customer.Name VIEW-AS ALERT-BOX BUTTONS OK.
Here the outer loop moves through Customer records and the inner loop moves through Order records associated with the current Customer. When the inner loop fails, the Order record buffer is not available to the CATCH associated with the inner loop. The Order buffer is scoped to the inner block and after the UNDO operation completes, the record is released and unavailable. The Customer buffer is scoped outside of the associated block (the inner DO block), so the CATCH can access this buffer and display the pending change to the Customer record. Since the CATCH itself raises error by attempting to display the unavailable Order data, those errors are thrown to the FOR EACH block and the change to the Customer record is itself undone.
This next example demonstrates the need to re-find the correct record if your CATCH block needs to work with the persistent data available in the associated block at the time the AVM raised error. The example is a variation of the example from the preceding section. This example includes an outer DO block with a reference to the Customer buffer. The re-find logic in the CATCH block is now in the IF AVAILABLE(Customer) branch of the IF statement.
The following is the code:
/* Defines an undoable variable because the NO-UNDO option is not specified. */
DEFINE VARIABLE TargetCustNum AS INTEGER.

/* The last valid value before the beginning of the DO block */
ASSIGN TargetCustNum = 1.

/* Outer DO */
DO ON ERROR UNDO, LEAVE:
    /* Customer buffer is scoped outside of the associated block */
FIND Customer WHERE CustNum = TargetCustNum.
    DISPLAY Customer.Name WITH SIDE-LABELS.

    /* Inner DO and also the associated block*/
DO ON ERROR UNDO, LEAVE:

/* This value will be undone on ERROR! */
ASSIGN TargetCustNum = 15.

/* Find a Customer */
FIND Customer WHERE Customer.CustNum = TargetCustNum.

/* Change the database! A transaction is now active. */
ASSIGN Customer.Name = Customer.NAME + " And Much More".

/* Confirm change to persistent field. */
MESSAGE "Name changed to: " Customer.Name
VIEW-AS ALERT-BOX BUTTONS OK.

/* ERROR raised. Control passes to CATCH block. */
FIND Order OF Customer WHERE OrderNum = 1234.

/* Statement will not execute. */
DISPLAY Customer.CustNum SKIP
Customer.Name SKIP
OrderNum SKIP
OrderStatus
VIEW-AS TEXT WITH SIDE-LABELS.

CATCH eSysError AS Progress.Lang.SysError:

/* Confirm if Customer record is available in CATCH. */
IF AVAILABLE (Customer) THEN DO:
MESSAGE "Customer record is still available."
VIEW-AS ALERT-BOX BUTTONS OK.

/* Re-Find the Customer to ensure buffer scoped to outer DO
block has correct data for inner DO block.
Cannot rely on value of TargetCustNum! */
FIND Customer WHERE Customer.CustNum = 15.

/* Confirm that change to database field was not committed
and UNDO variable was rolled back. */
MESSAGE "TargetCustNum = " TargetCustNum SKIP
"Name is now: " Customer.Name
VIEW-AS ALERT-BOX BUTTONS OK.
END. /* IF */
ELSE
MESSAGE "No Customer record is currently available."
VIEW-AS ALERT-BOX BUTTONS OK.
      END CATCH.
END. /* Inner DO */
END. /* Outer DO */