Page 1 of 1
Commiting a transaction closes all queries!
Posted: Fri 17 Apr 2015 15:25
by Marius2
Hello,
I'm trying to convert my application from anydac/firedac to unidac and i'm hitting a serious problem when closing a transaction.
I'm using a TUniQuery to display data (as a sort of memory dataset) and in a couple of steps further i'm using the same connection to retrieve a record via a second query (see code). While committing the transaction of the 2e query the memorydataset is also closed which is a total different behaviour than AnyDac. It will cause all kind of problems with our software.
Demo code:
Code: Select all
OldTransactionBehaviour := True; //The new transactions system does really not work for me (way to much work)..
displaydataset := TUniQuery.Create(nil);
displaydataset.Connection := MyConnection;
displaydataset.Sql.text := 'select * from whatever');
displaydataset.Open;
connection.StartTransaction;
lookupQuery := TUniQuery.Create(nil);
lookupQuery.Connection := MyConnection;
lookupQuery.Sql.text := 'select * from whatever');
lookupQuery.Open;
..something
connection.CommitTransaction; // <-- This closes the displaydataset and lookupquery as well (didn't expect that)..
The 'offending' code is in DbAccess.TDATransaction.CloseDataSets which iterates the Connection.DataSets and closes them. I notice the IsMultipleTransactionsSupported in the beginning of the loop which is implemented in TUniConnection.IsMultipleTransactionsSupported and TInterBaseUniProvider.IsDataSetNeedTransaction (alway's returning True in the case of Firebird).
Removing the offending line makes does make my code work, but what kind of problems a'm i introducting with this change? And what is the reason of this behaviour, whu is a second query spontaniously closed when commiting a connection (at least I think this is weird behaviour). Is this a bug??
Code: Select all
procedure TDATransaction.CloseDataSets;
var
i,j: Integer;
begin
for i := 0 to FConnections.Count - 1 do begin
if not FConnections[i].IsMultipleTransactionsSupported then
continue;
for j := 0 to FConnections[i].DataSetCount - 1 do
if FConnections[i].DataSets[j] is TCustomDADataSet then begin
//if TCustomDADataSet(FConnections[i].DataSets[j]).UsedTransaction = Self then
//TCustomDADataSet(FConnections[i].DataSets[j]).ConnectChange(FConnections[i], False);//We can't use SendConectEvent(False)
end
else
if FConnections[i].DataSets[j] is TDAMetaData then begin
if TDAMetaData(FConnections[i].DataSets[j]).UsedTransaction = Self then
TDAMetaData(FConnections[i].DataSets[j]).ConnectChange(FConnections[i], False);//We can't use SendConectEvent(False)
end;
for j := 0 to TCustomDAConnection(FConnections[i]).FSQLs.Count - 1 do
if TCustomDASQL(FConnections[i].FSQLs[j]).UsedTransaction = Self then
TCustomDASQL(TCustomDAConnection(FConnections[i]).FSQLs[j]).ConnectChange(FConnections[i], False);
end;
end;
Thanks,
Marius
Re: Commiting a transaction closes all queries!
Posted: Fri 17 Apr 2015 16:31
by FredS
You must use CommitRetaining with Firebird..
FB must have an open transaction at all times or all your Datasets will close.
Here class helpers are your friend with existing code..
Re: Commiting a transaction closes all queries!
Posted: Fri 17 Apr 2015 17:11
by Marius2
Yes, CommitRetaining. But other than that I really don't see the link why it should close existing datasets (OldTransactionBehaviour=False). What i'm I missing?
Code: Select all
while not qryCustomer.Eof
Connection.StartTransaction;
donate 1 dollar (insert into etc etc)
Connection.CommitTransaction; //Closes qryCustomer (thats cheap!)?
qryCustomer.Next;
end;
Re: Commiting a transaction closes all queries!
Posted: Fri 17 Apr 2015 17:23
by FredS
Commit ends the Transaction and FB can NOT be without a transaction.
I had an issue with Rollback so now I let my helper class do the work.
Code: Select all
//MMWIN:CLASSCOPY
unit _MM_Copy_Buffer_;
interface
type
{* TUniConnectionHelper: Wrapper maybe better than changing source code *}
TUniConnectionHelper = class helper for TUniConnection
{$REGION 'Notes'}
{Reintroduces these methods to allow us to easily implement Transactions that work with all Providers:
Dataset.AutoCommit := False; // Turn off InterBase AutoCommit, does nothing for SQLServer
with UniScript.Connection do
begin
....StartTransaction;
....try
.......do Stuff..
....except
......Rollback;
......Raise;
....end;
..Commit;
end}
{$ENDREGION}
private
public
procedure Commit;
procedure Rollback;
procedure StartTransaction;
end;
implementation
{* TUniConnectionHelper *}
procedure TUniConnectionHelper.Commit;
begin
if not InTransaction then Exit;
if (ProviderName = GetProviderName(dbFirebird)) then CommitRetaining
else if (ProviderName = GetProviderName(dbSQLServer)) then inherited
else raise Exception.CreateFmt('Provider: "%s" not implemented', [ProviderName]);
end;
procedure TUniConnectionHelper.Rollback;
begin
if not InTransaction then Exit;
if (ProviderName = GetProviderName(dbFirebird)) then RollbackRetaining
else if (ProviderName = GetProviderName(dbSQLServer)) then inherited
else raise Exception.CreateFmt('Provider: "%s" not implemented', [ProviderName]);
end;
procedure TUniConnectionHelper.StartTransaction;
begin
if not InTransaction then inherited;
end;
Re: Commiting a transaction closes all queries!
Posted: Sat 18 Apr 2015 08:38
by Marius2
Sorry Fred, thats a plain BS solution IMHO. Its an anwer on something that is not asked (the problem remains exactly the same in mysql, postgressql, sql server, and probably other databases as well). Datasets should not be spontaniously closed (unless the connection is closes for example)
About your helper class; Nested transaction can be helpfull, you are ignoring those. Silently ignoring rollbacks/commits without the proper call to starttransaction is just plain wrong! And did you btw notice the caption of this group? It has 'universal' in it so your 'solution' should really not be needed!
Re: Commiting a transaction closes all queries!
Posted: Sat 18 Apr 2015 16:49
by FredS
Marius2 wrote:It has 'universal' in it so your 'solution' should really not be needed!
No argument from my side on that.
I needed a solution to a problem and solved it in a few minutes. Arguing my point while waiting for a UniDAC release cycle was not an option.
Re: Commiting a transaction closes all queries!
Posted: Mon 20 Apr 2015 07:42
by ViktorV
Marius2 wrote:Hello,
I'm trying to convert my application from anydac/firedac to unidac and i'm hitting a serious problem when closing a transaction.
I'm using a TUniQuery to display data (as a sort of memory dataset) and in a couple of steps further i'm using the same connection to retrieve a record via a second query (see code). While committing the transaction of the 2e query the memorydataset is also closed which is a total different behaviour than AnyDac. It will cause all kind of problems with our software.
Demo code:
Code: Select all
OldTransactionBehaviour := True; //The new transactions system does really not work for me (way to much work)..
displaydataset := TUniQuery.Create(nil);
displaydataset.Connection := MyConnection;
displaydataset.Sql.text := 'select * from whatever');
displaydataset.Open;
connection.StartTransaction;
lookupQuery := TUniQuery.Create(nil);
lookupQuery.Connection := MyConnection;
lookupQuery.Sql.text := 'select * from whatever');
lookupQuery.Open;
..something
connection.CommitTransaction; // <-- This closes the displaydataset and lookupquery as well (didn't expect that)..
The 'offending' code is in DbAccess.TDATransaction.CloseDataSets which iterates the Connection.DataSets and closes them. I notice the IsMultipleTransactionsSupported in the beginning of the loop which is implemented in TUniConnection.IsMultipleTransactionsSupported and TInterBaseUniProvider.IsDataSetNeedTransaction (alway's returning True in the case of Firebird).
Removing the offending line makes does make my code work, but what kind of problems a'm i introducting with this change? And what is the reason of this behaviour, whu is a second query spontaniously closed when commiting a connection (at least I think this is weird behaviour). Is this a bug??
Code: Select all
procedure TDATransaction.CloseDataSets;
var
i,j: Integer;
begin
for i := 0 to FConnections.Count - 1 do begin
if not FConnections[i].IsMultipleTransactionsSupported then
continue;
for j := 0 to FConnections[i].DataSetCount - 1 do
if FConnections[i].DataSets[j] is TCustomDADataSet then begin
//if TCustomDADataSet(FConnections[i].DataSets[j]).UsedTransaction = Self then
//TCustomDADataSet(FConnections[i].DataSets[j]).ConnectChange(FConnections[i], False);//We can't use SendConectEvent(False)
end
else
if FConnections[i].DataSets[j] is TDAMetaData then begin
if TDAMetaData(FConnections[i].DataSets[j]).UsedTransaction = Self then
TDAMetaData(FConnections[i].DataSets[j]).ConnectChange(FConnections[i], False);//We can't use SendConectEvent(False)
end;
for j := 0 to TCustomDAConnection(FConnections[i]).FSQLs.Count - 1 do
if TCustomDASQL(FConnections[i].FSQLs[j]).UsedTransaction = Self then
TCustomDASQL(TCustomDAConnection(FConnections[i]).FSQLs[j]).ConnectChange(FConnections[i], False);
end;
end;
Thanks,
Marius
The similar question has already been discussed on our forum. Follow the link
http://forums.devart.com/viewtopic.php?f=28&t=25591 for details.
Re: Commiting a transaction closes all queries!
Posted: Fri 01 May 2015 10:53
by Marius2
Hello Viktor,
Thank you, I will try to investigate that some more (and reverse my changes in UniDac).
As a sidenote: I'm definitely not happy with these differences. I was under the impression that a product called "Universal DAC" should have hide the details of differences in transaction model for me. Via AnyDac I have like 4 databases systems and they all behave the same! I more or less expected the same behaviour from UniDac (because why should i have to know that a transaction in Firebird behaves different). That is the task of UniDac (why else choose that name?). Anywhay, could Devart please rethink about such strategies and put it on the wishlist maybe?
Regards,
Marius
Re: Commiting a transaction closes all queries!
Posted: Fri 01 May 2015 22:50
by marcodor
Commit closes all open cursors by definition. This can't be changed, ever. If you need memory datasets then use memory datasets. Fetch all data using unidirectional query into your memory datasets and enjoy.
What scenario do you expect for half fetched result set, after commit, if it stays "open", and later call next or post?
Re: Commiting a transaction closes all queries!
Posted: Tue 05 May 2015 10:04
by ViktorV
Marius2 wrote:Hello Viktor,
Thank you, I will try to investigate that some more (and reverse my changes in UniDac).
As a sidenote: I'm definitely not happy with these differences. I was under the impression that a product called "Universal DAC" should have hide the details of differences in transaction model for me. Via AnyDac I have like 4 databases systems and they all behave the same! I more or less expected the same behaviour from UniDac (because why should i have to know that a transaction in Firebird behaves different). That is the task of UniDac (why else choose that name?). Anywhay, could Devart please rethink about such strategies and put it on the wishlist maybe?
Regards,
Marius
In your sample, both datasets use the default transaction TIBCConnection. Therefore, when executing TIBCConnection.Commit or TIBCConnection.Rollback, these datasets are closed.
To prevent dataset closing when executing TIBCConnection.Commit or TIBCConnection.Rollback methods, you can use a separate transaction for it, for example:
Code: Select all
Tr1 := TUniTransaction.Create(nil);
Tr1.DefaultConnection := MyConnection;
displaydataset := TUniQuery.Create(nil);
displaydataset.Connection := MyConnection;
displaydataset.Transaction := Tr1;
displaydataset.Sql.Text := 'select * from whatever';
displaydataset.Open;
MyConnection.StartTransaction;
lookupQuery := TUniQuery.Create(nil);
lookupQuery.Connection := MyConnection;
lookupQuery.Sql.Text := 'select * from whatever';
lookupQuery.Open;
MyConnection.Commit;
Re: Commiting a transaction closes all queries!
Posted: Tue 05 May 2015 13:56
by Marius2
Many thank Victor,
Yes, I was thinking the same, it is however quite a lot of work to change the whole application(s) so for now we have changed the UniDac code. It really does not matter for our app as we will never ever use the unidac insert/update/delete sql commands and (the ones needed for updating memdatasets/clientdatasets etc.). Once we have introduced the extra transaction we can rollback the changes in UniDac sources.
Anywhay, we are getting there rapidly, and except for the somewhat unexpected differences with AnyDac we are happy with UniDac.
NB; Still need to change the tools with a crapload of bulk insert queries and measure that with AnyDac
Regards,
Marius
Re: Commiting a transaction closes all queries!
Posted: Wed 06 May 2015 08:46
by ViktorV
Thank you for your interest to our products.
Feel free to contact us if you have any further questions about UniDAC.