Previous Next

CATCH statement
Defines an error-handling end block for any undoable ABL block. An end block is an ABL block that can occur only within another block. The block containing the end block is known as the associated block. End-blocks must occur between the last line of executable code in the associated block and the END statement.
The CATCH end block executes when an error raised in the associated block is compatible with the error type specified in the CATCH block. To be compatible, the error type must be the error type specified in the CATCH statement, or it must be a sub-type (sub-class) of the specified type. CATCH blocks take precedence over any implicit or explicit ON ERROR directives for the associated block. This is the syntax for the CATCH statement and its related blocks:
Syntax 
 
block-statements
 
    CATCH error-variable AS [ CLASS ] error-class:
 
        catch-logic
 
    END [ CATCH ] .
 
[ block-end-statement ]
 
block-statements
All of the statements of an enclosing associated ABL block, except for its block-end-statement. The enclosing associated block can be any ABL block, including another CATCH block.
error-variable
The variable name that references the error object generated by the error condition. Typically, you do not define the error-variable ahead of time with the DEFINE VARIABLE statement. The AVM recognizes a new variable name on the CATCH statement as a new error-variable definition. Each CATCH in an associated block must have a unique error-variable. You can reuse an error-variable name in a different associated block, as long as its type is compatible with the new definition.
[ CLASS ] error-class
Typically Progress.Lang.SysError for system errors or Progress.Lang.AppError (or your subclass) for application errors. Optionally, you can provide the CLASS keyword.
catch-logic
All statements allowed in a CATCH block, which can include any valid ABL statement. For more information on CATCH block execution, see the notes for this reference entry.
block-end-statement
For all associated ABL blocks except a main external procedure block, the END statement terminating the enclosing associated block of the CATCH block. External procedure blocks have no terminating END statement.
Examples 
The following code fragment shows CATCH blocks for associated DO blocks:
 
DO ON ERROR UNDO, LEAVE:
  FIND FIRST Customer NO-LOCK WHERE Customer.CustNum = 5000.
 
  CATCH oneError AS Progress.Lang.SysError:
    MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
  END CATCH.
 
  CATCH twoError AS Progress.Lang.ProError:
    MESSAGE twoError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
  END CATCH.
END. /* FIRST DO */
 
DO ON ERROR UNDO, LEAVE:
  FIND FIRST Customer NO-LOCK WHERE Customer.CustNum = 6000.
 
  /* You can reuse an error-variable from a different associated block */
  CATCH oneError AS Progress.Lang.SysError:
    MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
  END CATCH.
 
  /* NOT LEGAL: Each CATCH block in an associated block must have a unique
     error-variable. */
  CATCH oneError AS Progress.Lang.ProError:
    MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
  END CATCH.
END. /* SECOND DO */
In the following example, the CATCH block will catch any ABL system error:
 
DEFINE VARIABLE iCust AS INTEGER NO-UNDO INITIAL 5000.
 
FIND Customer NO-LOCK WHERE Customer.CustNum = iCust. /* Will fail */
 
/* Won't execute because FIND fails */
MESSAGE "Customer found" VIEW-AS ALERT-BOX BUTTONS OK. 
 
/* The associated block for this CATCH block is the main block of the .p */
CATCH eSysError AS Progress.Lang.SysError:
  MESSAGE eSysError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
END CATCH.
Notes 
*
 
Syntax 
associated-block:
  .
  .
  .
  [ CATCH options :
    .
    .
    .
  END [ CATCH ] . ] ...
  [FINALLY :
    .
    .
    .
  END [ FINALLY ] . ]
END. /* associated-block */
Note that a CATCH block can also contain a CATCH or FINALLY block, just as a FINALLY block can contain a CATCH or FINALLY block. For more information on FINALLY blocks, see the FINALLY statement reference entry.
Thus, the following blocks can have a CATCH block:
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
ABL issues a compile-time error if a CATCH end block is present in a simple DO block, since simple DO blocks do not have error handling capabilities. DO blocks must have either TRANSACTION or an ON ERROR directive in order to have a CATCH. For example:
 
DO TRANSACTION
  . . .
  CATCH . . . :
    . . .
  END CATCH.
END.
*
The code within a CATCH block is only executed if an ERROR of type error-class (or a sub-type) is raised within the body of the associated block. This behavior is also true if any sub-routine called by the associated block returns or raises an error of type error-class. When ERROR is raised, if there is an active transaction, the associated block is undone before the AVM begins executing the statements within the CATCH block.
*
 
FOR EACH Customer:
  /* Code body of the associated block */
 
  /* This CATCH specifies the most specialized user-defined error class.
     It will catch only myAppError error objects or objects derived from
     myAppError. */
  CATCH eMyAppError AS Acme.Error.myAppError:
    /*Handler code for Acme.Error.myAppError condition. */
  END CATCH.
 
  /* This CATCH will handle Progress.Lang.AppError or any user-defined
     application error type, except for eMyAppError which would be
     handled by the preceding CATCH block. */
  CATCH eAppError AS Progress.Lang.AppError: 
    /* Handler code for AppError condition. */
  END CATCH.
 
  /* This CATCH will handle any error raised by an ABL statement. Since
     it inherits from the same object as AppError in the class hierarchy,
     this CATCH could come before or after the CATCH for AppError */
  CATCH eSysError AS Progress.Lang.SysError: 
    /* Handler code for SysError condition. */
  END CATCH.
 
    /* This will catch any possible error raised in the ABL. */
  CATCH eError AS Progress.Lang.Error: 
    /* Handler code for any error condition. */
  END CATCH.
END. /* FOR EACH Customer, associate block */
*
 
FOR EACH Customer:
  /* Code body of the associated block. */
 
  /*This will catch all application errors */
  CATCH eAppError AS Progress.Lang.AppError: 
    /* Handler code for AppError condition */
  END CATCH.
 
  /* Never get here, because myAppError is a subtype of
     Progress.Lang.AppError */
  CATCH eMyAppError AS Acme.Error.myAppError:
    /* Handler code for myAppError condition */
  END CATCH.
END. /* FOR EACH Customer, Associated Block */
*
If error is raised in a block and is not handled by a CATCH block, then the error is handled by the ON ERROR directive of the associated block. This could be an explicit ON ERROR phrase, or the implicit (default) ON ERROR directive for the block type.
*
A CATCH block can have a CATCH block within it. In this case, the contained CATCH block only handles errors raised within the CATCH block. To prevent infinite looping, any UNDO, THROW statement within the top-level CATCH block or any CATCH block nested within it immediately throws the error to the block that contains the associated block of the top-level CATCH block. For example:
 
FOR EACH Customer:
  /* FOR EACH code body */
 
  DO ON ERROR UNDO, LEAVE:
    /* DO code body */
    CATCH eAppError AS Progress.Lang.AppError: 
      /* CATCH code body */
 
      CATCH eSysError AS Progress.Lang.SysError:
        UNDO, THROW eSysError. /* Will be handled by CATCH anyError... */
      END CATCH.
    END CATCH.
  END. /* DO */
 
  CATCH anyError AS Progress.Lang.Error:
    /* Handler code for anyError condition */
  END CATCH.
END. /* FOR EACH Customer */
*
*
a)
b)
c)
The same behavior occurs for an explicit UNDO, THROW statement in a CATCH block. For example:
 
DO ON ERROR UNDO, LEAVE:
  /* Check for Orders */
 
  /* Fails and throws Progress.Lang.SysError. Execution goes to CATCH */
  FIND FIRST Order NO-LOCK WHERE Order.CustNum = 1000. 
  MESSAGE "Order found". /* MESSAGE does not execute */
 
  CATCH eSysError AS Progress.Lang.SysError:
    /* Check if Customer exists, which fails. ON ERROR UNDO, THROW for
       CATCH will raise ERROR in main block of .p - execution goes to
       CATCH in main block */*/
    FIND FIRST Customer NO-LOCK WHERE Customer.CustNum = 1000. 
  END CATCH.
END.
 
MESSAGE "Customer found". /* MESSAGE does not execute */
 
/* This CATCH is for the main block of the .p */
CATCH eSysError AS Progress.Lang.SysError:
  MESSAGE eSysError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
END CATCH.
*
*
In this example, DO TRANSACTION and CATCH both reference the Customer buffer:
 
/* myproc.p */
DEFINE VARIABLE myInt as INTEGER NO-UNDO INITIAL 5.
 
MESSAGE myInt AVAILABLE(Customer).     /* will see '5 no'*/
 
DO TRANSACTION ON ERROR UNDO, LEAVE:
  myInt = 10.
  FIND FIRST Customer NO-LOCK.
  MESSAGE myInt AVAILABLE(Customer).   /* will see '10 yes' */
 
/* Returns ERROR (throws Progress.Lang.AppError). The block is undone and
  the Customer buffer is released - execution goes to CATCH. */
  RUN Test.p (Customer.CustNum). 
  ...
 
  CATCH eAppError AS Progress.Lang.AppError:
    MESSAGE myInt AVAILABLE(Customer). /* will see '5 no'*/
  END CATCH.
END.
As the result of the reference to the Customer buffer in the CATCH block in the previous example, the scope of the Customer buffer is raised to the procedure level (myproc.p), since the smallest enclosing block of the DO TRANSACTION is the procedure block.
*
If you want LEAVE, NEXT, or RETRY to apply to the associated block of a CATCH block, you must use the existing label syntax for these statements.
An explicit UNDO, THROW in a CATCH block causes the AVM to raise ERROR in the block that encloses the associated block of the CATCH block; not the associated block itself.
In this example, LEAVE in the CATCH applies to the CATCH:
 
DEFINE VARIABLE iOrdNum        AS INTEGER NO-UNDO.
DEFINE VARIABLE lSomeCondition AS LOGICAL NO-UNDO.
 
FOR EACH Customer:
  UPDATE iOrdNum.
  FIND Order NO-LOCK WHERE Order.CustNum = Customer.CustNum 
    AND Order.OrderNum = iOrdNum.  /* Can fail and raise ERROR */
 
  /* Won't get here if FIND fails */
  DISPLAY Order.OrderNum Order.ShipDate. 
 
  CATCH eSysError AS Progress.Lang.SysError:
    MESSAGE "Order " iOrdNum " does not exist for Customer ".
    /* This LEAVE applies to the CATCH. Execution will retry the same
       customer */
    IF lSomeCondition THEN
      UNDO, LEAVE.
    ... 
    /* More statements in the CATCH that will execute if UNDO, LEAVE
       didn't execute */
  END CATCH.
END. /* FOR EACH Customer */
In this example, the procedure gives the user three chances to get the right order number:
 
DEFINE VARIABLE iOrdNum AS INTEGER NO-UNDO.
DEFINE VARIABLE iTries  AS INTEGER NO-UNDO INITIAL 1.
 
blk1:
FOR EACH Customer NO-LOCK:
  UPDATE iOrdNum.
  FIND Order NO-LOCK WHERE Order.CustNum = Customer.CustNum 
    AND Order.OrderNum = iOrdNum.  /* Can Fail and raise ERROR */
  ...
  CATCH eSysError AS Progress.Lang.SysError:
    MESSAGE "Order " iOrdNum " does not exist for Customer ".
 
    IF iTries <= 3 THEN
      iTries = iTries + 1.
    ELSE DO:
       MESSAGE "Too many tries for this Customer".
       iTries = 1.
       /* Leave the CATCH.  Execution will resume with the next
          iteration of the FOR EACH */
       UNDO, LEAVE blk1.
    END.
  END CATCH.
END. /* FOR EACH Customer */
In this example, LEAVE the FOR EACH in the occurrence of a PrinterDown application error:
 
DEFINE VARIABLE iOrdNum AS INTEGER NO-UNDO.
DEFINE VARIABLE iTries  AS INTEGER NO-UNDO INITIAL 1.
 
blk1:
FOR EACH Customer NO-LOCK:
  UPDATE iOrdNum.
 
  /* Can Fail and raise ERROR - execution will go to CATCH for
     Progress.Lang.SysError */
  FIND Order NO-LOCK WHERE Order.CustNum = Customer.CustNum 
    AND Order.OrderNum = iOrdNum.
 
  /* Successfully found Order. Try to print invoice. If PrintInvoice.p 
     throws an Acme.Error.PrinterDownError error, just leave the FOR EACH
     block. If PrintInvoice.p throws any other type of AppError, try with
     the next customer. */
  RUN PrintInvoice.p (INPUT Order.OrderNum).
  ...
 
  CATCH pde AS Acme.Error.PrinterDownError:
    MESSAGE "Printer down...aborting".
    UNDO, LEAVE blk1.
  END CATCH.
 
  CATCH eAppError AS Progress.Lang.AppError:
    MESSAGE "Problem printing invoice for order " iOrdNum.
    /* Leave the CATCH. Execution will resume with the next iteration of
       the FOR EACH */
    UNDO, NEXT blk1
  END CATCH.
END. /* FOR EACH Customer */
If there is no explicit flow-of-control statement in the CATCH block, the AVM will leave the CATCH block and execute the default error action for the associated block after executing the last statement in the CATCH block and any code within a FINALLY block. This means RETRY for all blocks. When no input-blocking statements are present, the AVM prevents infinite looping by changing the RETRY to NEXT for iterating blocks or LEAVE for non-iterating blocks.
Note:
In the following code, if an Acme.Error.myAppError is caught the explicit UNDO, THROW statement causes the caught error to be thrown to the block enclosing the FOR EACH (remember that UNDO, THROW in a CATCH means leave associated block, then throw). However, if a Progress.Lang.SysError is caught the AVM will execute a NEXT on the FOR EACH block. For example:
 
FOR EACH Customer ON ERROR UNDO, LEAVE:
  /* FOR EACH code */
 
  CATCH eSysError AS Progress.Lang.SysError:
    /* Handler code for SysError condition */
 
    /* RETRY on FOR EACH after leaving the CATCH, which becomes LEAVE if
       there are no I/O staements.*/
  END CATCH.
 
  CATCH myAppErr AS Acme.Error.myAppError:
    /* Handler code for myAppError condition */
 
    /* THROW error to block enclosing the FOR EACH */
    UNDO, THROW myAppErr.
  END CATCH.
END.
*
In this example, a CATCH handles the error and the error message is suppressed:
 
DEFINE VARIABLE myInt as INTEGER NO-UNDO INITIAL 5.
 
DO ON ERROR UNDO, LEAVE:
  /* Raises ERROR and throws Progress.Lang.SysError. Error message
     suppressed and execution goes to CATCH */
  FIND Customer 1000.
  MESSAGE "After Find".  /* Will not get here */
 
  CATCH eSysError AS Progress.Lang.SysError:
    /* Will display "** Customer record not on file. (138)" */
    MESSAGE eSysError:GetMessage(1) VIEW-AS ALERT-BOX.
    /* Leave the CATCH, then the DO block */
  END CATCH.
END.
In this example, there is no CATCH block that handles the error and the error message is not suppressed:
 
DEFINE VARIABLE myInt as INTEGER NO-UNDO INITIAL 5.
 
DO ON ERROR UNDO, LEAVE:
  /* Raises ERROR and displays "Customer record not on file. (138)" 
     UNDO, LEAVE the block */
  FIND Customer 1000. 
  MESSAGE "After Find". /* Won't get here */
 
  CATCH ae AS Progress.Lang.AppError:
    MESSAGE ae:GetMessage(1) VIEW-AS ALERT-BOX.
  END CATCH.
END.
See also 
BLOCK-LEVEL ON ERROR UNDO, THROW statement, ON ERROR phrase, RETURN statement, ROUTINE-LEVEL ON ERROR UNDO, THROW statement, UNDO statement

Previous Next
© 2013 Progress Software Corporation and/or its subsidiaries or affiliates.