WHENEVER instruction

Use the WHENEVER instruction to define how exceptions must be handled for the rest of the module.

Syntax

WHENEVER exception-class
 exception-action

where exception-class is one of:

{ [ANY] ERROR
| [ANY]  SQLERROR
|  NOT FOUND
|  WARNING
}

and exception-action is one of:

{ CONTINUE
| STOP
| CALL function 
| RAISE
| GOTO label
}
  1. function can be any function name defined in the program.
  2. label must be a label defined in the current program block (main, function or report routine).

Usage

The WHENEVER instruction defines the exception handling by associating an exception class with an exception action.

Important: The scope of a WHENEVER instruction is similar to a C preprocessor macro: It is local to the module defines the error handling for the rest of the module, unless a new WHENEVER instruction is encountered by the compiler, or a TRY/CATCH block is used.

This code example shows a typical WHENEVER instruction usage:

WHENEVER ERROR CONTINUE
DROP TABLE mytable -- SQL error will be ignored 
CREATE TABLE mytable ( k INT, c VARCHAR(20) )
WHENEVER ERROR STOP
IF SQLCA.SQLCODE != 0 THEN
   ERROR "Could not create the table..."
END IF

Exception classes ERROR and SQLERROR are synonyms (compatibility issue). The previous example could have used WHENEVER SQLERROR instead of WHENEVER ERROR.

Actions for classes ERROR, WARNING and NOT FOUND can be set independently:

WHENEVER ERROR STOP
WHENEVER WARNING CONTINUE
WHENEVER NOT FOUND GOTO not_found_handler 
...
When using the WHENEVER ... CALL function instruction, the program flow will go to the specified function and the return to the code block where the exception occured:
MAIN
    DEFINE x INTEGER
    WHENEVER ANY ERROR CALL error_handler
    -- WHENEVER handler takes effect
    LET x = 1/0
    DISPLAY "Back in MAIN..."
END MAIN

FUNCTION error_handler()
    DISPLAY "error_handler: ", STATUS
END FUNCTION

-- output:

error_handler:       -1202
Back in MAIN...
Note: In a WHENEVER ... CALL instruction, you do not handle to specify braces after the function name.
A TRY/CATCH blocks takes precedence over the last WHENEVER instruction, see the following example:
MAIN
    DEFINE x INTEGER
    WHENEVER ANY ERROR CONTINUE
    -- WHENEVER handler takes effect
    LET x = 1/0
    DISPLAY "WHENEVER: ", STATUS
    -- WHENEVER handler is hidden by TRY/CATCH block
    TRY
        LET x = 1/0
    CATCH
        DISPLAY "CATCH   : ", STATUS
    END TRY
    -- WHENEVER handler takes again effect
    CALL func()
END MAIN

FUNCTION func()
    DEFINE x INTEGER
    LET x = 1/0
    DISPLAY "WHENEVER: ", STATUS
END FUNCTION

-- Output:

WHENEVER:       -1202
CATCH   :       -1202
WHENEVER:       -1202
The RAISE option can be used to propagate exceptions to the caller, which typically traps the error in a TRY/CATCH block:
-- main.4gl
IMPORT FGL myutils
MAIN
    TRY
       -- Pass a NULL form name to get error -1110
       CALL mutils.open_form(NULL)
    CATCH
       DISPLAY "Error: ", status
    END TRY
END MAIN

-- myutils.4gl
FUNCTION open_form(fn)
    DEFINE fn STRING
    WHENEVER ERROR RAISE -- Propagate exceptions to caller
    OPEN FORM f1 FROM fn
END FUNCTION
Important: WHENEVER [ANY] ERROR RAISE is not supported in a REPORT routine.