Commiting a transaction closes all queries!

Discussion of open issues, suggestions and bugs regarding UniDAC (Universal Data Access Components) for Delphi, C++Builder, Lazarus (and FPC)
Post Reply
Marius2
Posts: 22
Joined: Thu 19 Nov 2009 12:17

Commiting a transaction closes all queries!

Post by Marius2 » Fri 17 Apr 2015 15:25

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

FredS
Posts: 272
Joined: Mon 10 Nov 2014 17:52

Re: Commiting a transaction closes all queries!

Post by FredS » Fri 17 Apr 2015 16:31

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..

Marius2
Posts: 22
Joined: Thu 19 Nov 2009 12:17

Re: Commiting a transaction closes all queries!

Post by Marius2 » Fri 17 Apr 2015 17:11

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;

FredS
Posts: 272
Joined: Mon 10 Nov 2014 17:52

Re: Commiting a transaction closes all queries!

Post by FredS » Fri 17 Apr 2015 17:23

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;

Marius2
Posts: 22
Joined: Thu 19 Nov 2009 12:17

Re: Commiting a transaction closes all queries!

Post by Marius2 » Sat 18 Apr 2015 08:38

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!

FredS
Posts: 272
Joined: Mon 10 Nov 2014 17:52

Re: Commiting a transaction closes all queries!

Post by FredS » Sat 18 Apr 2015 16:49

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.

ViktorV
Devart Team
Posts: 3168
Joined: Wed 30 Jul 2014 07:16

Re: Commiting a transaction closes all queries!

Post by ViktorV » Mon 20 Apr 2015 07:42

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.

Marius2
Posts: 22
Joined: Thu 19 Nov 2009 12:17

Re: Commiting a transaction closes all queries!

Post by Marius2 » Fri 01 May 2015 10:53

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

marcodor
Posts: 29
Joined: Tue 29 Oct 2013 20:30

Re: Commiting a transaction closes all queries!

Post by marcodor » Fri 01 May 2015 22:50

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?

ViktorV
Devart Team
Posts: 3168
Joined: Wed 30 Jul 2014 07:16

Re: Commiting a transaction closes all queries!

Post by ViktorV » Tue 05 May 2015 10:04

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;

Marius2
Posts: 22
Joined: Thu 19 Nov 2009 12:17

Re: Commiting a transaction closes all queries!

Post by Marius2 » Tue 05 May 2015 13:56

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

ViktorV
Devart Team
Posts: 3168
Joined: Wed 30 Jul 2014 07:16

Re: Commiting a transaction closes all queries!

Post by ViktorV » Wed 06 May 2015 08:46

Thank you for your interest to our products.
Feel free to contact us if you have any further questions about UniDAC.

Post Reply