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.Locatecreates a clone of the underlying Ado Recordset for later use, and saves it. We can therefore replace the Locate byrecordsetClone := FDetail.Recordset.Clone(adLockReadOnly);(whichuses Winapi.ADOInt) and get the same error. - Inside
FMaster.Next, the exception gets thrown by trying to requeryFDetailafter the new parameter for MasterId is set. This happens inTCustomADODataSet.RefreshParams. - When you disconnect Detail from Master, everything works just fine. Setting the parameter of
FDetailby 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;