Exception Handling

Exception Handling

Top  Previous  Next

 

An exception is a named event, typically an error, that can be caught and handled by an application. Exceptions are thrown either by use of the THROW statement in an application program or automatically by QM as an alternative to the abort events normally raised by data type errors and similar failures.

 

The actual name of an exception has no significance to QM but should ideally relate to the situation that the exception handles. Exception names are case insensitive and may be up to 63 characters. Application developers should avoid creating exception names that begin with "SYS." and names containing a $ symbol as these are reserved as described below.

 

QMBasic allows creation of exception handlers in a manner broadly similar to that of some other programming languages by use of a TRY/CATCH construct:

 

TRY {statement}

{statements}

CATCH exception {, exception...}

{statements}

CATCH exception {, exception...}

{statements}

END

 

where

 

statement(s)are any executable QMBasic statements.

 

exceptionis an exception name. The name may optionally be enclosed in quotes. An exception name may be followed by the keyword DUMPING to cause a process dump file to be created prior to unwinding the stack back to the exception handler.

 

 

A single TRY construct may have multiple CATCH clauses, each handling one or more exceptions.

 

The TRY/CATCH construct executes the statement(s) in the TRY clause, monitoring for named exceptions being thrown. The TRY clause may include GOSUB, CALL or EXECUTE to execute lower level code outside of this clause, however, unless an exception is raised, it is essential that these operations return to complete the TRY clause. Use of GOTO to jump into or out of the TRY clause may have undesirable results.

 

On throwing an exception, the application exits from all lower level programs, subroutines, etc and continues execution in the CATCH clause that handles this exception. Where an object oriented programming object is discarded by an exception, the DESTROY.OBJECT subroutine will be executed in the normal way.

 

Transactions started by actions within the TRY clause and still active at the point when the exception is thrown will be rolled back.

 

 

A single CATCH clause may catch multiple exception names. On arrival in the CATCH clause, the @EXCEPTION variable will contain the name of the exception that has been caught and the @EXCEPTION.DATA variable will contain any data associated with this exception. The @EXCEPTION.ORIGIN variable is set to a dynamic array in which field 1 holds the program name from which the exception was thrown and field 2 holds the line number in that program. If the program has no cross-reference tables, the line number will be -1.

 

The CATCH clause has two optional qualifiers that can appear after an exception name. The DUMPING qualifier causes a process dump file to be created, showing the full state of the call stack at the point when the exception was thrown. The SAVING.STACK qualifier sets @EXCEPTION.STACK to contain the call stack in the same form as produced by the SYSTEM(1002) function. See TRY/CATCH for an example of a simple code fragment to report this data in a useful form.

 

 

Example

 

LOOP

  READNEXT ACC.ID ELSE EXIT

  TRY

     CALL PROCESS.ACCOUNT

  CATCH ACCOUNT.INVALID

     DISPLAY 'Account ' : ACC.ID : ' is not valid'

  END

REPEAT

 

The above rather simple example shows a program fragment that processes successive items from a select list. If the PROCESS.ACCOUNT subroutine, or any lower level action called from it, throws an ACCOUNT.INVALID exception, the program will continue execution at the DISPLAY statement.

 

 

Exception Groups

 

An exception name may be formed from multiple components separated by a period where each component forms a level of grouping. In the above example, the ACCOUNT.INVALID exception can be considered as part of an exception group named ACCOUNT. The CATCH clause in the example will be entered on throwing the specific ACCOUNT.INVALID exception. Alternatively, the CATCH clause could have been written as

CATCH ACCOUNT

and this would be executed for any exception in the ACCOUNT group, including the ACCOUNT.INVALID exception. Use of exception groups can significantly simplify programs by using a single CATCH clause to trap a whole exception group, the actual exception name being available in the @EXCEPTION variable. There is no restriction on the number of levels in an exception group aside from the entire exception name being limited to 63 characters. The example above would also catch further exception levels such as ACCOUNT.INVALID.CLOSED to show the reason for the account to be invalid.

 

 

Scope of Exception Handlers

 

The structure of an application may validly nest TRY/CATCH constructs to any depth. On throwing an exception, the system works back through the established exception handlers to find the first that catches the relevant exception. This process will not continue past a program that uses the TRAPPING ABORTS option of the EXECUTE statement as this option requests that any errors in lower level programs should cause return from the EXECUTE. An application can test whether there is a handler for a specific named exception by use of the CAUGHT() function.

 

 

The SYS$ANY Exception

 

A CATCH clause that references the SYS$ANY exception name will catch any exception and can be used as a general error handler. When used, this exception name must be the last one referenced in the CATCH clauses associated with the TRY. The exception name returned via @EXCEPTION will be that of the actual exception thrown, not SYS$ANY.

 

This exception can be used in much the same way as the TRAPPING ABORTS option of the EXECUTE statement to act as a firewall beyond which no exceptions will be thrown.

 

 

The SYS$UNHANDLED Exception

 

A CATCH clause that references the SYS$UNHANDLED exception name will be executed if there is no specific handler for the thrown exception or SYS$ANY. The check for this handler occurs after checking the entire exception handler stack for the named exception or SYS$ANY, therefore SYS$ANY effectively takes priority over SYS$UNHANDLED regardless of their relative positions in the exception handler stack.

 

 

System Generated Exceptions

 

Many of the situations that would otherwise cause an abort event to be generated such as a data type error can be trapped by exceptions from the SYS exception group.

 

If there is no handler for the exception, including the SYS$ANY and SYS$UNHANDLED handlers described above, the application will abort in the usual way. Note that the abort error message is stored in both @ABORT.MESSAGE and @EXCEPTION.DATA but is not displayed if it is caught by an exception handler.

 

The full list of exception names in the SYS group is shown hierarchically below. In all cases, the name is formed by adding the desired hierarchical elements to a "SYS." prefix. For example, a deadlock situation throws an exception named "SYS.LOCKS.DEADLOCK".

 

ABORT

User initiated abort

EXTCALL

External Call Interface

  ABORT

Abort requested by external call handler

  INVALID_ARGCT

Incorrect argument count

  IO_ERROR

Data transfer error

  SETUP_ERROR

Child process creation failed

  SIZE_ERROR

Invalid data size

FILESYS

File system

  CREATION_ERROR

Error creating file

  INVALID_FILE_TYPE

Invalid file type for this operation

  INVALID_PARTNO

Invalid distributed file part number

  INVALID_PATHNAME

Pathname is invalid

  INVALID_RECORD_ID

Record id is invalid

  IO_ERROR

Data transfer error

  LOCK_REQUIRED

Record is not locked

  NOT_DISTRFL

Distributed file required

  OPEN_ERROR

Error opening file

  PERMISSIONS

Permissions error

     ERROR

Permissions do not allow this operation

     READ_ONLY

File is open in read-only mode

  TRIGGER

Trigger function errors

     ERROR

Error code returned by trigger function

     INVALID_ARGCT

Incorrect argument count in trigger function

KERNEL

Kernel errors

  NO_MEMORY

Insufficient memory available

LOCKS

Locking errors

  DEADLOCK

Deadlock detected

  MAXRLOCK

Maximum number of record locks in process reached

  TASK_LOCK

Task lock errors

     INVALID

Invalid task lock number

PRINTER

Printer and terminal errors

  IO_ERROR

Data transfer error

  INVALID_KEY

Invalid key value

  INVALID_OPERATION

Operation not permitted

  INVALID_REGION

Invalid screen region

  INVALID_UNIT

Invalid print unit

PROGRAM

QMBasic errors

  DATATYPE

Data type errors

     ECS_CHAR

Data outside 8-bit character set not valid

     INVALID

Data type is invalid for this usage

     INVALID_SOCKET

Invalid socket type for this usage

     NOT_COLLECTION

Data collection variable required

     NOT_FILE

File variable required

     NOT_IMAGE

Screen image variable required

     NOT_MATRIX

Dimensioned matrix required

     NOT_NUMERIC

Non-numeric where numeric required  (Not thrown if NON.NUMERIC.WARNING option is enabled)

     NOT_OBJECT

Instantiated object required

     NOT_SEQ_FILE

Sequential file variable required

     NOT_SOCKET

Socket variable required

     NOT_SORT

Sort session variable required

     NULL_STRING

Null string is invalid for this usage

     UNASSIGNED

Variable is unassigned

  DIV_ZERO

Divide by zero  (Not thrown if DIV.ZERO.WARNING option is enabled)

  INVALID_ARGCT

Incorrect argument count

  INVALID_CALL_NAME

Invalid subroutine/function/class name

  INVALID_DATA_SIZE

Invalid data size for this operation

  INVALID_IN_TXN

Operation is not valid in a transaction

  INVALID_ITYPE

I-type object code is invalid

  INVALID_KEY

Invalid key value in function call

  INVALID_MATRIX_INDEX

Matrix index is out of range

  INVALID_MATRIX_SIZE

Invalid matrix dimensions

  INVALID_NAME

Invalid data collection element name

  INVALID_OBJECT_REFERENCE

Invalid object method/property reference

  IS_CLASS

Program is a class module

  LIMIT

System limit reached

     CALL_DEPTH

Subroutine call depth limit

     NUM_FILES

System file count limit

     GOSUB_DEPTH

Internal subroutine depth limit

     MAX_BREAKPOINTS

Debugger breakpoint limit

  LOAD_ERROR

Error loading program

     READ_ERROR

Error reading object code

     VERSION

Unsupported object code version

  NO_PDEBUG

Phantom debugger not active

  NO_PROPERTY_VALUE

Object did not return property value

  NOT_FOUND

Program not found

  NOT_OBJECT

Program is not an instantiated object

  NOT_TXN

Operation is only valid in a transaction

  RANGE

Value is out of range for this operation

  RESIZE_ERROR

Error resizing common or matrix

SELECT

Select list processing errors

  INVALID_LIST_NO

Invalid select list number

SHELL

Shell command execution errors

  EXECUTION_ERROR

Command execution error

SORT

Sort subsystem errors

  INVALID_DATA_LENGTH

Sort data exceeds maximum allowed length

  INVALID_KEY_COUNT

Invalid number of sort keys

  INVALID_KEY_LENGTH

Sort key exceeds maximum allowed length

  INVALID_OPERATION

Sort operation invalid at this time

  IO_ERROR

Data transfer error

 

 

See also:

CAUGHT(), THROW, TRY/CATCH