This package contains a modified enhanced version of Interbase Express Components 6.08 for Delphi 6 and Borland C++ Builder 6.
The design of the components didn't allow to make the changes using inheritance. So the changes were made in source code of the original components. The changes, made by me, are quoted with comments {<ov...} and {ov...>}.
Component TIBCustomDataset has new property
public property WriteTransaction: TIBTransaction;
- writing transaction. The old Transaction property corresponds to the reading transaction.
SelectSQL and GeneratorFieldSQL of the dataset execute in reading transaction, and DeleteSQL, InsertSQL, ModifySQL, RefreshSQL - in writing transaction.
When you assign some value to Transaction property and WriteTransaction is equal to old value of Transaction, the new value is assigned to WriteTransaction property too. Thus both reading and writing occurs in one transaction, and the compatibility with the old behavior of TIBCustomDataset is kept.
WriteTransaction doesn't start automatic when TIBCustomDataset opens. If WriteTransaction is not active when preparing TIBCustomDataset, DeleteSQL, InsertSQL, ModifySQL, RefreshSQL commands are prepared in reading transaction.
New property
protected property SingleWriteTransactions: Boolean default false;
was added to TIBCustomDataset. If this property is true and WriteTransaction is not active, TIBCustomDataset starts and commits a transaction for every data modification command. However for cached updates all changes are applied in one transaction start-commit.
TIBCustomDataset introduces as well new events
protected property BeforeWriteTransactionEnd: TNotifyEvent property AfterWriteTransactionEnd: TNotifyEvent property WriteTransactionFree: TNotifyEvent
by analogy with reading transaction.
TIBDataSet, TIBTable, TIBStoredProc, TIBQuery inherit the new TIBCustomDataset behavior and publishes new TIBCustomDataset properties and events:
published property WriteTransaction; property SingleWriteTransactions; property BeforeWriteTransactionEnd; property AfterWriteTransactionEnd; property WriteTransactionFree;
Known issue: there are problem with blob fields when using different reading and writing transactions. Don't use so far this feature with datasets containing blob fields.
In module IBDatabase.pas new delegate type is declared:
type TEndTransactionEvent = procedure(Sender: TObject; Action: TTransactionAction) of object;
where
type TTransactionAction = (TARollback, TACommit, TARollbackRetaining, TACommitRetaining);
New virtual methods of TIBTransaction component are added
protected procedure DoBeforeStartTransaction; virtual; procedure DoAfterStartTransaction; virtual; procedure DoBeforeEndTransaction(Action: TTransactionAction); virtual; procedure DoAfterEndTransaction(Action: TTransactionAction); virtual;
that call new appropriate events
published property BeforeStartTransaction: TNotifyEvent read FBeforeStartTransaction write FBeforeStartTransaction; property AfterStartTransaction: TNotifyEvent read FAfterStartTransaction write FAfterStartTransaction; property BeforeEndTransaction: TEndTransactionEvent read FBeforeEndTransaction write FBeforeEndTransaction; property AfterEndTransaction: TEndTransactionEvent read FAfterEndTransaction write FAfterEndTransaction;
TIBSQLMonitor writes to log the transaction names of executed statements as well.
Original IBX 6.08 has errors determining the fact of connection loss and than fails into infinite recursion trying to close datasets.
In this version more acceptable reaction on connection loss is implemented.
In a case of connection loss new virtual method of TIBDatabase
protected procedure DoConnectionLost(var Terminate: Boolean); virtual;
is called, that in turn calls event handler for
published property OnConnectionLost: TIBOnConnectionLost read FOnConnectionLost write FOnConnectionLost;
where
TIBOnConnectionLost = procedure(Database: TIBDatabase; var Terminate: Boolean) of object;
OnConnectionLost handler can show message to the user and set Terminate parameter to true (the default is false). This kills the application using ExitProcess, no destructors or other cleanup are executed. In my opinion, it's the easiest and best way.
If no handler for OnConnectionLost is provided or the handler does not set Terminate to true, all open datasets are silently (without any exceptions) closed, all active transaction ends, and finally EIBInterBaseError exception throws.
According to the paper by Alexander Nevsky (in Russian), from early versions the Interbase has a bug in API isc_dsql_execute2 function, as a result the function under some conditions fails. Yet in FIBC by Gregory H.Deatz there was a (doubtful) quick workaround for this problem. It consists in second call to isc_dsql_execute2 when first call has failed. Almost not changed, this "feature" was safe kept in IBX 6.08 (see TIBSQL.ExecQuery in ibsql.pas).
The consequences of this second call of stored procedure, that remains a secret for a programmer, are hard to overestimate :). Fortunately, the second call fails too usually, and well designed application rolls back a transaction. However, various variants are possible...
The bug in isc_dsql_execute2 was eliminated in build 606 open source branch of the Interbase and never existed in Firebird. I don't know if Borland's Interbase branch still have it - I use Firebird for a long time and could not find this info in Internet. You can check it, calling various procedures in one transaction in a loop, for example (from the article abovementioned):
Create Procedure P1(IPar Integer) Returns (OPar Integer) As Begin Opar=IPar; End Create Procedure P2(IPar Integer) Returns (OPar1 Integer, OPar2 Integer) As Begin OPar1=IPar; OPar2=IPar+1; End Procedure TestSPCall; Var I: Integer; begin IBTransaction1.StartTransaction; For I:=1 to 10 do begin IBStoredProc1.ParamByName('IPar').AsInteger:=I; // IBStoredProc1.Prepare; IBStoredProc1.ExecProc; // IBStoredProc1.UnPrepare; IBStoredProc2.ParamByName('IPar').AsInteger:=I; // IBStoredProc2.Prepare; IBStoredProc2.ExecProc; // IBStoredProc2.UnPrepare; end; IBTransaction1.Commit; end
However that may be, it is unlikely that this workaround is better as the original bug, so it's time to eliminate it. That is done in this modification of the components, the second call to isc_dsql_execute2 is removed.
If you want to uninstall this packages, uninstall them from IDE as usual and remove corresponding Library and Include paths. Then run setup files for original IBX 6.08, that you can download from Jeff Overcash page at Borland Code Central.
All code is provided "as is".
In no event shall the author of the modifications be liable for any special, incidental, indirect, consequential, punitive or exemplary damages whatsoever (including, without limitation, damages for loss of business profits or business interruption, goodwill, loss of business information, or any other pecuniary loss) whether based on principles of contract, tort (including negligence), duty, indemnity, contribution or otherwise, arising out of the use of or inability to use the software or the provision of or failure to provide support services.
If you aren't agree with this limitation, you should not use this software.
© Oleg V. Pashchenko 2004 www.ov-soft.com