Revisited: TClientDataset "Missing data provider or data packet" - delphi

With a dynamically created TFDQuery,TClientDataSet, and TDataSetProvider I bump into the "Missing data provider or data packet" with this code:
procedure ResetSavedPasswords(ADataModuleDataBaseAdmin : TDataModuleDataBaseAdmin);
var
lQuery : TFDQuery;
lCDS : TClientDataSet;
lProvider : TDataSetProvider;
begin
lFrmBezig := TFormBezig.Create(nil);
lQuery := TFDQuery.Create(nil);
lProvider := TDataSetProvider.Create(Application);
lCDS := TClientDataSet.Create(nil);
try
lQuery.Connection := ADataModuleDataBaseAdmin.FDConnectionTimeTell;
lQuery.CachedUpdates := true;
lProvider.Options := lProvider.Options - [poUseQuoteChar];
lProvider.DataSet := lQuery;
lProvider.Name := 'prvResetSavedPW';
lCDS.ProviderName := lProvider.Name;
lQuery.SQL.Text := Format('select %s,%s from <owner>%s',[sMedMedID,sMedSavedPassword,SMedTabelNaam]),ADataModuleDataBaseAdmin;
lCDS.Open;
Note that the created TDataSetProvider has an owner, based on this answer:
If DatasetProvider has no owner, ClientDataSet can not obtain a reference to the provider
But I still get the error. Opening the TFDQuery first shows me it has data.
What can be the reason?
Using FireDAC with Delphi 10.4. Sydney in a Win32 app.

It turns out that TClientDataSet needs an owner too:
lCDS := TClientDataSet.Create(Application);
This is obvious from the code that triggered the exception:
function TCustomClientDataSet.GetAppServer: IAppServer;
var
ProvComp: TComponent;
DS: TObject;
begin
if not HasAppServer then
begin
if ProviderName <> '' then
if Assigned(RemoteServer) then
FAppServer := RemoteServer.GetServer
else if Assigned(ConnectionBroker) then
FAppServer := ConnectionBroker.GetServer
else
begin
if Assigned(Owner) then
begin
ProvComp := Owner.FindComponent(ProviderName);
if Assigned(ProvComp) and (ProvComp is TCustomProvider) then
begin
DS := GetObjectProperty(ProvComp, 'DataSet');
if Assigned(DS) and (DS = Self) then
DatabaseError(SNoCircularReference, Self);
FAppServer := TLocalAppServer.Create(TCustomProvider(ProvComp));
end;
end;
end;
if not HasAppServer then
DatabaseError(SNoDataProvider, Self);
end;
Result := FAppServer;
end;
The Assigned(Owner) fails, so the code does not bother looking for the TDataSetProvider

Related

FireDAC BatchMove from MemoryTable

My incoming data is loaded in a TFDMemTable, (the reader). The writer is a TFDQuery.
Incoming data should be inserted if not in the target, otherwise updated. Matches are based on the UUID field.
I am unable to properly define that the UUID field is the key.
Here is a code example - does not work. FBatchMove.Execute fails because cannot it find any key fields.
procedure TSubDB.FindDestRecord(ASender: TObject; var AFound: Boolean);
var
aSrc: TBytes;
begin
SetLength(aSrc, 16);
aSrc := FReader.DataSet.FieldByName('UUID').AsBytes;
AFound := FWriter.DataSet.Locate('UUID', aSrc, []);
end;
function TSubDB.LoadDB(const aFilename: string): boolean;
var
FQry: TFDQuery;
FBatchMove: TFDBatchMove;
FReader: TFDBatchMoveDataSetReader;
FWriter: TFDBatchMoveDataSetWriter;
FMemTable: TFDMemTable;
begin
FQry := TFDQuery.Create(nil);
FQry.Connection := dmFB.myDB;
FQry.FetchOptions.AssignedValues := [evItems];
FQry.FetchOptions.Items := [fiBlobs, fiDetails];
FBatchMove := TFDBatchMove.Create(nil);
FBatchMove.Analyze := [taDelimSep, taHeader, taFields];
FReader := TFDBatchMoveDataSetReader.Create(FBatchMove);
FWriter := TFDBatchMoveDataSetWriter.Create(FBatchMove);
FMemTable := TFDMemTable.Create(nil);
try
FMemTable.LoadFromFile(aFileName, sfBinary);
//Not sure how to make the BatchMove recognize that UUID is the key for OnFindDestRecord
FMemTable.IndexFieldNames := 'UUID';
with FMemTable.Indexes.Add do
begin
Name :='idxUUID';
Fields := 'UUID';
Active := true;
end;
FMemTable.IndexName := 'idxUUID';
FMemTable.IndexesActive := true;
FMemTable.FieldByName('UUID').ProviderFlags := FMemTable.FieldByName('UUID').ProviderFlags + [pfInKey];
FReader.DataSet := FMemTable;
FQry.SQL.Text := 'select * from test';
FWriter.DataSet := FQry;
FBatchMove.OnFindDestRecord := FindDestRecord;
FBatchMove.Mode := dmAppendUpdate;
//None of the above seems to keep the pfInKey in the UUID field's ProviderFlags
FBatchMove.Execute;
FQry.Open;
FQry.Close;
finally
FMemTable.Free;
FWriter.Free;
FReader.Free;
FBatchMove.Free;
FQry.Free;
end;
end;
I would really appreciate a working example of batch move (where the target has data, so the batch move mode is dmAppendUpdate).
The key here is that the writer needs to be a TFDBatchMoveSQLWriter with a TableName set. This way the destination had the primary key defined and it is then used to decide whether to insert or update.
function TSubDB.LoadDB(const aFilename: string): boolean;
var
FQry: TFDQuery;
FBatchMove: TFDBatchMove;
FReader: TFDBatchMoveDataSetReader;
FWriter: TFDBatchMoveSQLWriter;
FMemTable: TFDMemTable;
begin
FQry := TFDQuery.Create(nil);
FQry.Connection := dmFB.myDB;
FQry.FetchOptions.AssignedValues := [evItems];
FQry.FetchOptions.Items := [fiBlobs, fiDetails];
FBatchMove := TFDBatchMove.Create(nil);
FBatchMove.Analyze := [taDelimSep, taHeader, taFields];
FReader := TFDBatchMoveDataSetReader.Create(FBatchMove);
FWriter := TFDBatchMoveSQLWriter.Create(FBatchMove);
FMemTable := TFDMemTable.Create(nil);
try
FMemTable.LoadFromFile(aFileName, sfBinary);
FReader.DataSet := FMemTable;
FQry.SQL.Text := 'select * from test';
FWriter.Connection := dmFB.myDB;
FWriter.TableName := 'test';
FBatchMove.Mode := dmAppendUpdate;
FBatchMove.Execute;
FQry.Open;
FQry.Close;
finally
FMemTable.Free;
FWriter.Free;
FReader.Free;
FBatchMove.Free;
FQry.Free;
end;
end;

Is Platform Assistant Server necessary to run fireDAC application to connect to Informix?

I am trying to connect my Delphi application to Informix database using fireDAC. I all the parameters supplied in connection editor. But I have to run PA Server to make it work.
So is it necessary to run the PA Server to connect to Informix db.
I am able to solve it using the following code to connect instead of dragging droping he controls:
procedure TForm1.FormCreate(Sender: TObject);
var
Params: TStringList;
begin
FDManager := TFDManager.Create(self);
FDconnection := TFDConnection.Create(self);
FDQuery := TFDQuery.Create(self);
FDataSOurce := TDataSource.Create(self);
Params := TStringList.create;
Params.Values['User_Name'] := paramstr(3);
Params.Values['Database'] := paramstr(2);
Params.Values['Password'] := paramstr(4);
Params.Values['DriverName'] := 'Informix';
Params.Values['HostName'] := paramstr(1);
Params.Values['RDBMS'] := 'OTHER';
Params.Values['DriverID'] := 'TDBX';
FDManager.AddConnectionDef('BOSSConnection', 'TDBX', Params);
FDConnection.DriverName := 'TDBX';
FDConnection.ConnectionDefName:='BOSSConnection';
FDConnection.Connected := True;
FDQuery.SQL.Add('select first 10 cust_code, bus_name, status from strcustr;');
FDQuery.Connection := FDConnection;
FDataSource.DataSet := FDQuery;
FDQuery.Active := True;
DBGrid1.DataSource := FDataSource;
FDConnection.LoginPrompt := False;
end;

Paradox error on CreateTable

I was creating table during execution, however at the creation of the table, (see my code)
procedure CreateTempTable(pDataBaseName,pSessionName:String);
begin
//-------create "TempTable"
TempTable:=TTable.Create(application);
With TempTable Do
begin
//-------set false in "Active"
Active := False;
//-------name of Session
SessionName:=pSessionName;
//-------name of DataBase.
DatabaseName :=pDataBaseName;
Filtered := True;
//-------name of table
TableName := 'TempTabl.DB';
//-------paradox type
TableType := ttParadox;
//-------if the table is already exists
if TempTable.Exists then
//-------delete the table
TempTable.DeleteTable;
//-------create 2 fields "Field1" & "Field2"
with FieldDefs do
begin
Clear;
with AddFieldDef do
begin
DataType := ftFloat;
Name := 'Field1';
end;
with AddFieldDef do
begin
DataType := ftFloat;
Name:='Field2';
end;
end;
//-------Create table
CreateTable; // Here where the exception bursts off
end;
end;
an exception is raised, which is: "Table is open, Table does not exist".
so what is exactly the problem, is it open or doesnot exist?
This is the exception:
(inside With TempTable Do) There is no need to delete the TempTabl.DB explicit. Is overwritten by CreateTable anyway.
To test if the table is not being used by other components in the IDE, you can try to delete the file TempTabl.DB.
CreateTempTable is now a function CreateTempTable(pDataBaseName,pSessionName:String):Boolean;
So you can better handle errors.
Tested with delphi 5 and RAD Studio 2007.
function CreateTempTable(pDataBaseName,pSessionName:String):Boolean;
begin
result:=false;
// assume pDataBaseName=directory, or change it
if FileExists(pDataBaseName+'TempTabl.DB') then begin
if NOT DeleteFile(pDataBaseName+'TempTabl.DB') then begin
showMessage('Table opened by another part of the IDE');
exit;
end;
end;
TempTable:=TTable.Create(application);
With TempTable Do
begin
Active := False;
SessionName:=pSessionName;
DatabaseName :=pDataBaseName;
//Filtered := True;
TableName := 'TempTabl.DB';
TableType := ttParadox;
with FieldDefs do
begin
Clear;
with AddFieldDef do
begin
DataType := ftFloat;
Name := 'Field1';
end;
with AddFieldDef do
begin
DataType := ftFloat;
Name:='Field2';
end;
end;
CreateTable;
result:=true;
end;
end;

How to auto fill in editlabel field with specific database row delphi

Hi there I have a problem
I need to auto fill in information from the database, but if i do it like this:
leemail.text := dm.atInlog['email'];
lenaam.text := dm.atInlog['naam'];
leAdres.text := dm.atInlog['adres'];
lePostcode.text := dm.atInlog['postcode'];
leTelefoonnummer.text := dm.atInlog['telefoon'];
leWoonplaats.Text := dm.atInlog['Woonplaats']
It just takes the first row. I want a specific row.
I can make it work with a button like this:
procedure TfmKlant.BTGegevensClick(Sender: TObject);
begin
//vraag gegevens van gebruiker op
dm.atInlog.Open;
while (not gevonden) and (not dm.atInlog.eof) do
begin
if dm.atInlog['email'] = fminloggen.inlognaam
then
begin
// plaats gegevens in de textboxen
gevonden := true;
leemail.text := dm.atInlog['email'];
lenaam.text := dm.atInlog['naam'];
leAdres.text := dm.atInlog['adres'];
lePostcode.text := dm.atInlog['postcode'];
leTelefoonnummer.text := dm.atInlog['telefoon'];
leWoonplaats.Text := dm.atInlog['Woonplaats']
end
else dm.atInlog.Next;
end;
But It does not do this in create form. How can I auto fill in the labeledit with the requested data?
thanks in advance
You could use TDataSet.Locate or Lookup:
type
TfmKlant = class(TForm)
// ... other declarations
private
procedure ShowData(p_Email: string);
end;
...
procedure TfmKlant.FormCreate(Sender: TObject);
begin
// assuming the data set is already open, and fminloggen.inlognaaem is already set
if dm.atInLog.Locate('email', fminloggen.inlognaam, []) then
begin
ShowData(fminloggen.inloognam);
end;
end;
procedure TfmKlant.ShowData(p_Email: string);
begin
gevonden := true;
leemail.text := dm.atInlog['email'];
lenaam.text := dm.atInlog['naam'];
leAdres.text := dm.atInlog['adres'];
lePostcode.text := dm.atInlog['postcode'];
leTelefoonnummer.text := dm.atInlog['telefoon'];
leWoonplaats.Text := dm.atInlog['Woonplaats']
end;

Delphi: Clientdataset: EDatabaseError on .Open; with ProviderName set

So I'm having this code that processes what the client sends on a pattern. If he sends 'getBENUds', the server sends the DataSet for this table back using the SaveToString method.
Then, this is sent to the client. (I'm using Synapse).
procedure TTCPSocketThrd.Execute;
var s: String;
strm: TMemoryStream;
ADO_CON: TADOConnection;
ADO_QUERY: TADOQuery;
DS_PROV: TDataSetProvider;
DS_CLIENT: TClientDataSet;
begin
CoInitialize(nil);
Sock := TTCPBlockSocket.Create;
try
Sock.Socket := CSock;
Sock.GetSins;
with Sock do
begin
repeat
if terminated then break;
s := RecvTerminated(60000,'|');
if s = 'getBENUds' then
begin
//ini ADO_CON
ADO_CON := TADOConnection.Create(Form1);
ADO_CON.ConnectionString := 'not for public';
ADO_CON.LoginPrompt := false;
ADO_CON.Provider := 'SQLOLEDB.1';
ADO_CON.Open;
//ini ADO_QUERY
ADO_QUERY := TADOQuery.Create(ADO_CON);
ADO_QUERY.Connection := ADO_CON;
//ini DS_PROV
DS_PROV := TDataSetProvider.Create(ADO_CON);
DS_PROV.DataSet := ADO_QUERY;
//ini DS_CLIENT
DS_CLIENT := TClientDataSet.Create(ADO_CON);
DS_CLIENT.ProviderName := 'DS_PROV';
//SQLQUERY Abfrage
ADO_QUERY.SQL.Clear;
ADO_QUERY.SQL.Add('SELECT * FROM BENU');
ADO_QUERY.Open;
//DSCLIENTDATASET bauen
strm := TMemoryStream.Create;
DS_CLIENT.Open;
DS_CLIENT.SaveToStream(strm);
end
else if s = 'getBESTEds' then
...
The line it says: DS_CLIENT.Open an exception is thrown:
An exception has been thrown: class EDatabaseError. Text: 'missing data-provider or data package'.
The data-provider has been set as can be seen above to 'DS_PROV', so it has to be the missing data package.
But shouldn't the ClientDataSet get its data from the DataSetProvider which in turn gets it from the ADOQuery that gets the data from the database?
This is as far as I get with my level of knowledge. I hope its not too difficult, because in my eyes, everything I did was correct.
Use
DS_CLIENT.SetProvider(DS_PROV);
or after DS_PROV creation: (at this time your component has really no name)
DS_PROV.name := 'DS_PROV';

Resources