To test basic functionality
All the queries used the same IBQuery component.
The original code below worked fine until the server was restarted ... then it would only generate errors. The patched code (with lots of try..except blocks) runs without any errors.
I do not know if better code can be written (I always assume that it can), but as far as I know, it works.
Note: If you have the wrong IBX version, your Delphi program simply crashes and Windows terminates it. You must have a recent version for the following code to work.
procedure TBasic_DataModule.SetDatabase(Database: string); begin if Database = '' then exit; if CompareText(IBDatabase_Basic.DatabaseName, Database) <> 0 then begin IBDatabase_Basic.Connected := false ; IBDatabase_Basic.DatabaseName := Database; IBDatabase_Basic.Connected := true; end; end; // SetDatabase procedure TBasic_DataModule.ExecuteSelectSQL(s: string); begin IBQuery_Basic.Close; if IBTransaction_Query_Basic.InTransaction then IBTransaction_Query_Basic.Rollback; IBQuery_Basic.SQL.Text := s; // stops error if the database is not found if IBDatabase_Basic.Connected then IBQuery_Basic.Open; end; // ExecuteSelectSQL
procedure TBasic_DataModule.SetDatabase(Database: string); begin if Database = '' then exit; try if CompareText(IBDatabase_Basic.DatabaseName, Database) <> 0 then begin IBDatabase_Basic.ForceClose; // this works after *Lost connection* // IBDatabase_Basic.Connected := false ; // this fails after *Lost connection* IBDatabase_Basic.DatabaseName := Database; end; IBDatabase_Basic.Connected := true; // This fails if the network is still bad except on E: Exception do begin HandleIBErrors(e.Message, nil); end; end; end; // SetDatabase
procedure TBasic_DataModule.ExecuteSelectSQL(s: string); begin try IBQuery_Basic.Close; // may generate *lost connection* error except on E: Exception do begin HandleIBErrors(e.Message, IBQuery_Basic); end; end; // by definition, each selectSQL must be a new transaction try if IBTransaction_Query_Basic.InTransaction then IBTransaction_Query_Basic.Rollback; except on E: Exception do begin HandleIBErrors(e.Message, IBQuery_Basic); end; end; try IBQuery_Basic.SQL.Text := s; except on E: Exception do begin HandleIBErrors(e.Message, IBQuery_Basic); IBQuery_Basic.SQL.Text := s; end; end; try IBQuery_Basic.Open; except on E: Exception do begin HandleIBErrors(e.Message, IBQuery_Basic); IBQuery_Basic.Open; end; end; end; // ExecuteSelectSQL
// This handles network failures // *Display_Error* is provided to suppress the error dialog box for web servers function TBasic_DataModule.Database_Connected(Display_Error: boolean): boolean; begin Result := false; // needed to stop a compiler warning // *TestConnected* automatically disconnects if there is a problem if IBDatabase_Basic.TestConnected then begin Result := true; end else begin try IBDatabase_Basic.Connected := true; if IBDatabase_Basic.Connected then begin Result := true; end; except Result := false; exit; end; end; if (Result = false) and (Display_Error = true) then begin // this displays a modal dialog box that stops the application - for test only // must not display this with web pages (CGI and ISAPI files) Application.MessageBox( PChar('Database not available' + ' ' + Database), 'Database Error', MB_OK); end; end; // Database_Connected // This function helps to plug a design problem in the IBX components // If there is either a temporary or permanent loss of database connection // this sets *connected* to false (handle = nil) and fixes other problems function TBasic_DataModule.HandleIBErrors(E_msg: string; ds: TIBCustomDataSet): boolean; begin result := false; if (E_msg = 'connection lost to database') or (E_msg = 'unavailable database') or (E_msg = 'unassigned code') then begin IBDatabase_Basic.ForceClose; try IBDatabase_Basic.Connected := true; // may or may not work except on E: Exception do begin // eat a possible *Connected := true* exception end; end; result := true; end; if ( (E_msg = 'Dataset open') or (E_msg = 'unassigned code')) // these frequently appear with loss connection and (ds <> nil) then begin try ds.Fields.Clear; ds.active := true; // Only way to set FOpen := False - this causes another exception except on E: Exception do begin // eat the expected *ds.active := true* exception end; end; result := true; end; end; // HandleIBClientErrors
HandleIBErrors Notes
IBQuery.active := true;
IBQuery.FOpen := FalseIn every case, this causes another exception which is captured. (ForceClose does not clear this flag.)
IBQuery.Fields.Clear;the query component can not be reused for a different query. The previous query will still work ... but not a new query.