Detailed Description

An Exception indicate an error condition from which recovery may be possible.

The Library raise exceptions, which can be handled by recovery code, if recovery is possible. When an exception is raised, it is handled by the handler that was most recently instantiated. If no handlers are defined an exception will cause the library to call its abort handler to abort with an error message.

Handlers are instantiated by the TRY-CATCH and TRY-FINALLY statements, which are implemented as macros in this interface. These statements handle nested exceptions and manage exception-state data. The syntax of the TRY-CATCH statement is,

The TRY-CATCH statement establish handlers for the exceptions named e1, e2,.., en and execute the statements S. If no exceptions are raised by S, the handlers are dismantled and execution continues at the statement after the END_TRY. If S raises an exception e which is one of e1..en the execution of S is interrupted and control transfers immediately to the statements following the relevant CATCH clause. If S raises an exception that is not one of e1..en, the exception will raise up the call-stack and unless a previous installed handler catch the exception, it will cause the application to abort.

Here's a concrete example calling a method in the libzdb API which may throw an exception. If the method Connection_execute() fails it will throw an SQLException. The CATCH statement will catch this exception, if thrown, and log an error message

The TRY-FINALLY statement is similar to TRY-CATCH but in addition adds a FINALLY clausal which is always executed, regardless if an exception was raised or not. The syntax of the TRY-FINALLY statement is,

closes the database Connection regardless if an exception was thrown or not by the code in the TRY-block. The above example also demonstrate that FINALLY can be used without an exception handler, if an exception was thrown it will be rethrown after the control reaches the end of the finally block. Meaning that we can cleanup even if an exception was thrown and the exception will automatically propagate up the call stack afterwards.

Finally, the RETURN statement, defined in this interface, must be used instead of C return statements inside a try-block. If any of the statements in a try block must do a return, they must do so with this macro instead of the usual C return statement.

Exception details

Inside an exception handler, details about an exception is available in the variable Exception_frame. The following demonstrate usage of this variable to provide detailed logging of an exception. For SQL errors, Connection_getLastError() can also be used, though Exception_frame is recommended since in addition to SQL errors, it also cover API errors not directly related to SQL.

Volatile and assignment inside a try-block

A variable declared outside a try-block and assigned a value inside said block should be declared volatile if the variable will be accessed from an exception handler. Otherwise the compiler will/may optimize away the value set in the try-block and the handler will not see the new value. Declaring the variable volatile is only necessary if the variable is to be used inside a CATCH or ELSE block. Example:

Thread-safe

The Exception stack is stored in a thread-specific variable so Exceptions are made thread-safe. This means that Exceptions are thread local and an Exception thrown in one thread cannot be catched in another thread. This also means that clients must handle Exceptions per thread and cannot use one TRY-ELSE block in the main program to catch all Exceptions. This is only possible if no threads were started.