Page 1 of 1

Cached Updates error

Posted: Fri 11 May 2007 18:17
by abadia
Hi there!

I'm a brazilian Delphi developer and I guess I have found a bug on SDAC/ODAC 3.80.0.37:
The error occurs when I use the UpdateAction uaSkip on method OnUpdateRecord.

You can see below how the error happens:
1 - OnPostRecord of the dataset I execute ApplyUpdates;
2 - OnUpdateRecord I skip the record when it is in InsertMode, and the field 'au_LName' is empty
3 - To get the error I insert a new record, fill another field (not 'au_LName'), edit other record and go back to the 'skipped record'. When I try to post the changes of the skipped record the application raises an 'Access Violation'.

For more details see the following code:

Code: Select all

program Project1;

uses
  Forms, DB, DBAccess, MSAccess, MemDS, Grids, DBGrids;

{$R *.res}

var
  MainForm: TForm;
  Query: TMSQuery;
  Grid: TDBGrid;

type
  TProcs = class
  public
    procedure UpdateRecord(DataSet: TDataSet; UpdateKind:
      TUpdateKind; var UpdateAction: TUpdateAction);
    procedure AfterPost(DataSet: TDataSet);
  end;

procedure TProcs.UpdateRecord(DataSet: TDataSet; UpdateKind:
    TUpdateKind; var UpdateAction: TUpdateAction);
begin
  case UpdateKind of
    ukModify,
    ukInsert:
      if Query.FieldByName('au_lname').IsNull then
        UpdateAction := uaSkip
      else
        UpdateAction := TUpdateAction(uaDefault);
    ukDelete:
      UpdateAction := TUpdateAction(uaDefault);
  end;
end;

procedure TProcs.AfterPost(DataSet: TDataSet);
begin
  Query.Connection.StartTransaction;
  try
    Query.ApplyUpdates;
    Query.Connection.Commit;
    Query.CommitUpdates;
  except
    Query.RestoreUpdates;
    Query.Connection.Rollback;
    raise;
  end;
end;

begin
  Application.Initialize;
  Application.CreateForm(TForm, MainForm);
  MainForm.Width := 800;
  MainForm.Position := poDesktopCenter;

  Query := TMSQuery.Create(MainForm);
  Query.Connection := TMSConnection.Create(MainForm);
  Query.Connection.Server := '.';
  Query.Connection.Database := 'pubs';
  Query.Connection.Username := 'sa';
  Query.Connection.Password := '';
  Query.SQL.Text := 'select * from authors';
  Query.CachedUpdates := True;
  Query.OnUpdateRecord := TProcs(nil).UpdateRecord;
  Query.AfterPost := TProcs(nil).AfterPost;
  Query.Open;

  Grid := TDBGrid.Create(MainForm);
  Grid.Parent := MainForm;
  Grid.Top := 10;
  Grid.Left := 10;
  Grid.Height := MainForm.ClientHeight - 20;
  Grid.Width := MainForm.ClientWidth - 20;
  Grid.DataSource := TDataSource.Create(MainForm);
  Grid.DataSource.DataSet := Query;
  Application.Run;
end.
One question more:
Is there a version 3.80.0.37 for Delphi 2007? (I'm using Sdac std edition).

Posted: Mon 14 May 2007 12:00
by Jackson
Thank you for information.
We have reproduced the problem and fixed it.
This fix will be included in the next build of SDAC 3 and ODAC 5.
Please watch for announcements at the forum.
SDAC 3 doesn't support Delphi 2007. Support for this version of Delphi was added in SDAC 4.

Posted: Mon 21 May 2007 11:39
by abadia
Hello!

Thanks for the bug fix!
Do you have a milestone for the next build of SDAC 3 with this correction?

I've seen that the correction was made just on SDAC 4! Please, why this error hasn't been corrected on version 3?

Posted: Tue 22 May 2007 14:43
by Jackson
The new build of SDAC 3 will be available in about a week.

Posted: Wed 06 Jun 2007 12:30
by abadia
Any news???

Posted: Fri 08 Jun 2007 09:03
by Antaeus
The new build of SDAC 3 is already available for download.

Posted: Mon 03 Sep 2007 11:51
by abadia
Hi,

The reported bug is keeping in build 38 of SDAC 3 (and ODAC), just not raise a exception very rarely, but affect subsequent updates.

I have a correction for this bug and I'd like to send to you. What e-mail may I send it?

Posted: Mon 03 Sep 2007 14:43
by Antaeus
I checked the this issue again. I cannot reproduce this problem with SDAC 3.80.0.38. Could you provide me with the code that reproduces the problem? Probably the fix did not fix the problem completely.

We received your e-mail, but we must reproduce the problem in order to consider applying your fix.

Posted: Tue 04 Sep 2007 12:39
by abadia
I add only a monitor in my sample:

Code: Select all

program Project1;

uses
  Forms, DB, DBAccess, MSAccess, MemDS, Grids, DBGrids, MSSQLMonitor;

{$R *.res}

var
  MainForm: TForm;
  Query: TMSQuery;
  Grid: TDBGrid;

type
  TProcs = class
  public
    procedure UpdateRecord(DataSet: TDataSet; UpdateKind:
      TUpdateKind; var UpdateAction: TUpdateAction);
    procedure AfterPost(DataSet: TDataSet);
  end;

procedure TProcs.UpdateRecord(DataSet: TDataSet; UpdateKind:
    TUpdateKind; var UpdateAction: TUpdateAction);
begin
  case UpdateKind of
    ukModify,
    ukInsert:
      if Query.FieldByName('au_lname').IsNull then
        UpdateAction := uaSkip
      else
        UpdateAction := TUpdateAction(uaDefault);
    ukDelete:
      UpdateAction := TUpdateAction(uaDefault);
  end;
end;

procedure TProcs.AfterPost(DataSet: TDataSet);
begin
  Query.Connection.StartTransaction;
  try
    Query.ApplyUpdates;
    Query.Connection.Commit;
    Query.CommitUpdates;
  except
    Query.RestoreUpdates;
    Query.Connection.Rollback;
    raise;
  end;
end;

begin
  Application.Initialize;
  Application.CreateForm(TForm, MainForm);
  MainForm.Width := 800;
  MainForm.Position := poDesktopCenter;

  TMSSQLMonitor.Create(MainForm).Active := True;

  Query := TMSQuery.Create(MainForm);
  Query.Connection := TMSConnection.Create(MainForm);
  Query.Connection.Server := '.';
  Query.Connection.Database := 'pubs';
  Query.Connection.Username := 'sa';
  Query.Connection.Password := '';
  Query.SQL.Text := 'select * from authors';
  Query.CachedUpdates := True;
  Query.OnUpdateRecord := TProcs(nil).UpdateRecord;
  Query.AfterPost := TProcs(nil).AfterPost;
  Query.Open;

  Grid := TDBGrid.Create(MainForm);
  Grid.Parent := MainForm;
  Grid.Top := 10;
  Grid.Left := 10;
  Grid.Height := MainForm.ClientHeight - 20;
  Grid.Width := MainForm.ClientWidth - 20;
  Grid.DataSource := TDataSource.Create(MainForm);
  Grid.DataSource.DataSet := Query;
  Application.Run;
end.
To view the problem, run it with monitor.
In my database, the first line is "Johnson White". Press INSERT and fill only the au_fname field with "Abadia". Press DOWN. (Monitor shows start and commit transaction only - no update because the record was skipped).
Now, modiffy "Johnson" to "Johnsonn" and press UP. (Monitor shows start and commit transaction and the update command - the Johnson's record was modified correctly).
Once more, modify the Johnson's record back to original (Johnsonn to Johnson) and press DOWN. Look the monitor: The update is not executed! Only start and commit transaction.

Explanation:
The current code in CommitUpdates is losing the pointer for the last item in cache. In the reality, LastCacheItem points for an invalid position in memory. Depending on the pointed position, an access violation can occur or not, but the next CacheItens created are lost and never are updated or destroyed.
My fix keep track the LastCacheItem variable correctly and this not occur.

In my e-mail I request you delete the call a ApplyUpdates in CommitUpdates method because I think it is a developer responsibility, and in codes using skipped records cause a slow performance by double call of ApplyUpdates (one by developer and another by component).

Posted: Tue 04 Sep 2007 14:14
by Antaeus
Thank you for your explanation. We reproduced the problem now we are working on it. As soon as we solve it, we will let you know.

Posted: Tue 11 Sep 2007 11:00
by Antaeus
The fix you have sent us by e-mail is correct. We will apply it to the next build of MyDAC with one correction. We will not remove the lines calling ApplyUpdates, as this fix changes behaviour of the CommitUpdates method. This may lead to data loss in many applications based on MyDAC.

Thank you for collaboration.

Posted: Tue 11 Sep 2007 12:51
by abadia
Why don't you remove the ApplyUpdates? Doesn't it exist on MyDAC?

You could declare CommitUpdates as virtual and then override it on MyDAC implementation to call ApplyUpdates.

I don't think it's correct that SDAC and ODAC users to be panalized because MySQL doesn't work in standard form.

Another solution is to put an option to disable this behavior.

By the way, when new build will be released?

Posted: Wed 12 Sep 2007 11:57
by Antaeus
I just misprinted in my previous post. There should be SDAC instead of MyDAC. However, this correction will affect behaviour of all our DAC products (ODAC, SDAC, MyDAC, IBDAC). As I mentioned, this may lead to data loss in applications based on DACs. We will think about adding such option in the future builds/versions.
By the way, when new build will be released?
The new builds of MyDAC 5.10 and ODAC 6.10 will be available within several days.
The new builds of MyDAC 4 and ODAC 5 will be available later. Now we have cannot provide exact terms.

Posted: Wed 12 Sep 2007 17:06
by abadia
I've read in your documentation that the "any"DAC was created as a replacement of BDE, thus should be work like it.

The developers that use the BDE knew that they would have to use ApplyUpdates before CommitUpdates and "any"DAC's documentation recommend this too.

I do not believe that it would have data loss if you made this change. Only inform about this on the next release notes.

Posted: Thu 13 Sep 2007 14:55
by Antaeus
You are right, in this case SDAC is incompatible with BDE, and call to ApplyUpdates within CommitUpdates in some cases may affect performance of CommitUpdates. Unfortunately now we cannot change this behaviour as this change will make SDAC and others incompatible with previous versions. We will consider a possibility to change this behaviour before the next version of our products.
Now we will document this behaviour in the help.