Exception after Locating a Detail TAdoQuery

Viewed 109

I have a master and a detail TAdoQuery called FMaster and FDetail. They are connected via the DataSource FMasterSource. The detail query has a parameter, which is automatically filled and updated depending on the master query.

Now the problem is: After doing a FDetail.Locate (or in fact FDetail.Recordset.Clone), FMaster.Next throws an exception while trying to requery FDetail with the new parameter:

  try
    FMaster.Open;
    FDetail.Open;
    FDetail.Locate('Id', 2, []);
    FMaster.Next; {<== throws Exception with ADO Errorcode 0x80040e05}
  finally
    FDetail.Close;
    FMaster.Close;
  end;

According to this List, Errorcode 0x80040e05 means "Object already open".

  • The problem seems to be, that FDetail.Locate creates a clone of the underlying Ado Recordset for later use, and saves it. We can therefore replace the Locate by recordsetClone := FDetail.Recordset.Clone(adLockReadOnly); (which uses Winapi.ADOInt) and get the same error.
  • Inside FMaster.Next, the exception gets thrown by trying to requery FDetail after the new parameter for MasterId is set. This happens in TCustomADODataSet.RefreshParams.
  • When you disconnect Detail from Master, everything works just fine. Setting the parameter of FDetail by hand works just fine.

What did I miss? Is it an error in the VCL? Or is it yet another weird ADO bug?

Source Code

I am using Oracle 11g XE and Delphi 10.4 Sydney.

Oracle tables:

drop table Detail;
drop table Master;

create table Master (
  Id NUMBER(2) not null, 
  constraint MasterPK primary key (Id)
);
create table Detail (
  Id       NUMBER(2) not null, 
  MasterId NUMBER(2), 
  constraint DetailPK primary key (Id),
  constraint DetailFK foreign key (MasterId) references Master(Id)
);
insert into Master values (1);
insert into Master values (2);
insert into Detail values (1,1);
insert into Detail values (2,1);
insert into Detail values (3,2);

Delphi code:

procedure TForm1.Button1Click(Sender: TObject);
begin
  //Initializing...
  FConnection.Provider := 'OraOLEDB.Oracle.1';
  FConnection.ConnectionString := 'Provider=OraOLEDB.Oracle.1;Password=xxxx;Persist Security Info=True;User ID=TEST;Data Source=XE';
  FConnection.LoginPrompt := False;
  FConnection.KeepConnection := False;

  FMaster.Connection := FConnection;
  FMaster.SQL.Text := 'select Id from Master';
  FMaster.CursorLocation := clUseServer;
  FMaster.CursorType := ctStatic;

  FMasterSource.DataSet := FMaster;

  FDetail.Connection := FConnection;
  FDetail.DataSource := FMasterSource;
  FDetail.SQL.Add('select Id, MasterId from Detail where MasterId = :Id');
  FDetail.Parameters[0].DataType := ftInteger;
  FDetail.CursorLocation := clUseServer;

  //Do stuff
  try
    FMaster.Open;
    FDetail.Open;
    //FDetail.Locate('Id', 2, []);
    recordsetClone := FDetail.Recordset.Clone(adLockReadOnly);
    FMaster.Next; {<== throws Exception with ADO Errorcode 0x80040e05}
  finally
    FDetail.Close;
    FMaster.Close;
  end;
end;
0 Answers
Related