Icon Exception Handling and Errors

Introduction
One of the first items to address in any application, and especially a database application, is how to anticipate and gracefully handle exceptions. This is true as well with DBISAM. Fortunately, Delphi and C++ both provide elegant exception types and handling. DBISAM uses this exception handling architecture and also expands upon it in several important ways. In certain situations DBISAM will intercept exceptions and trigger events in order to allow for the continuation of a process without the interruption that would occur if the exception were allowed to propagate through the call stack.

DBISAM Exception Types
DBISAM primarily uses the EDBISAMEngineError object as its exception object for all engine errors. This object descends from the EDatabaseError exception object defined in the common DB unit, which itself descends from the common Exception object. This hierarchy is important since it allows you to isolate the type of error that is occurring according to the type of exception object that has been raised, as you will see below when we demonstrate some exception handling.

Information DBISAM also raises certain component-level exceptions as an EDatabaseError to maintain consistency with the way the common DB unit and TDataSet component behaves. These mainly pertain to design-time property modifications, but a few can be raised at runtime also.

The EDBISAMEngineError object contains several important properties that can be accessed to discover specific information on the nature of the exception. The ErrorCode property is always populated with a value which indicates the error code for the current exception. Other properties may or may not be populated according to the error code being raised, and a list of all of the error codes raised by the DBISAM engine along with this information can be found in Appendix B - Error Codes and Messages.

Exception Handling
The most basic form of exception handling is to use the try..except block (Delphi) or try..catch (C++) to locally trap for specific error conditions. The error code that is returned when an open fails due to access problem is 11013, which is defined as DBISAM_OSEACCES in the dbisamcn unit (Delphi) or dbisamcn header file (C++). The following example shows how to trap for such an exception on open and display an appropriate error message to the user:

begin
   with MyDBISAMTable do
      begin
      DatabaseName:='c:\testdata';
      TableName:='customer';
      Exclusive:=True;
      ReadOnly:=False;
      try
         Open;
      except
         on E: Exception do
            begin
            if (E is EDatabaseError) and (E is EDBISAMEngineError) then
               begin
               if (EDBISAMEngineError(E).ErrorCode=DBISAM_OSEACCES) then
                  ShowMessage('Cannot open table '+TableName+
                    ', another user has the table open already')
               else
                  ShowMessage('Unknown or unexpected '+
                    'database engine error # '+
                    IntToStr(EDBISAMEngineError(E).ErrorCode));
               end
            else
               ShowMessage('Unknown or unexpected '+
                    'error has occurred');
            end;
      end;
      end;
end;

Exception Events
Besides trapping exceptions with a try..except or try..catch block, you may also use a global TApplication.OnException event handler to trap database exceptions. However, doing so will eliminate the ability to locally recover from the exception and possibly retry the operation or take some other course of action. There are several events in DBISAM components that allow you to code event handlers that remove the necessity of coding try..except or try..catch blocks while still providing for local recovery. These events are as follows:

EventDescription
OnEditErrorThis event is triggered when an error occurs during a call to the TDBISAMTable or TDBISAMQuery Edit method . The usual cause of an error is a record lock failure if the current session is using the pessimistic locking protocol (the default). Please see the Updating Tables and the Locking and Concurrency topics for more information on using this event and the DBISAM locking protocols.
OnDeleteErrorThis event is triggered when an error occurs during a call to the TDBISAMTable or TDBISAMQuery Delete method. The usual cause of an error is a record lock failure (a record lock is always obtained before a delete regardless of the locking protocol in use for the current session). Please see the Updating Tables and Query Result Sets and the Locking and Concurrency topics for more information on using this event and the DBISAM locking protocols.
OnPostErrorThis event is triggered when an error occurs during a call to the TDBISAMTable or TDBISAMQuery Post method. The usual cause of an error is a key violation for a unique index or the violation of a table constraint, however it can also be triggered by a record lock failure if the locking protocol for the current session is set to optimistic. Please see the Updating Tables and the Locking and Concurrency topics for more information on using this event and the DBISAM locking protocols.
OnQueryErrorThis event is triggered when an error occurs during the preparation or execution of an SQL statement or script via the TDBISAMQuery ExecSQL or Open methods. If this event is assigned an event handler then it will get triggered and the event handler will have the option of aborting the current SQL statement or script. If this event is not assigned an event handler then this event will not be triggered and the exception will be raised.
OnDataLostThis event is triggered when an error occurs during the alteration of a table's structure via the TDBISAMTable AlterTable or AddIndex methods, or via the execution of the ALTER TABLE or CREATE INDEX SQL statements by the TDBISAMQuery ExecSQL method. An error can be caused by key violations, field deletions, field conversion problems, table constraint failures, and any other type of problem during these operations. The OnDataLost event allows you to react to these errors by cancelling the current operation, continuing, or continuing without triggering this event anymore.

The above events are all based in the TDBISAMTable or TDBISAMQuery components, and are mainly geared toward application-level exception handling. There is a lower level of exception handling available also in the following TDBISAMEngine events:

EventDescription
OnInsertErrorThis event is triggered whenever an exception occurs during the insertion of any record in any table. The event handler for this event can choose to retry, abort, or fail the insert operation that raised the exception.
OnUpdateErrorThis event is triggered whenever an exception occurs during the update of any record in any table. The event handler for this event can choose to retry, abort, or fail the update operation that raised the exception.
OnDeleteErrorThis event is triggered whenever an exception occurs during the deletion of any record in any table. The event handler for this event can choose to retry, abort, or fail the delete operation that raised the exception.

Information If any exception is raised in an BeforeInsertTrigger, AfterInsertTrigger, BeforeUpdateTrigger, AfterUpdateTrigger, BeforeDeleteTrigger, or AfterDeleteTrigger event, the exception will be converted into an EDBISAMEngineError exception object with an error code of DBISAM_TRIGGERERROR. The original exception's error message will be assigned to the ErrorMessage property of the EDBISAMEngineError exception object, as well as be included as part of the error message in the EDBISAMEngineError exception object itself.
Image