Delphi 10.3.3 FireDAC FDConnection.GetTableNames Interbase - delphi-10.3-rio

I am trying to put some FireDAC orders to a unit. I realized an FDConnection with a create order.
My problem: I always get a failure using GetTableNames.
See procedure FD_DBTabellen_holen.
What i am doing wrong?
Thanks a lot
Martin
interface
type
TSW_FDDataBase = class
constructor Init;
destructor Done;
private
oFDConnection : TFDConnection;
//oFDTransaction : TFDTransaction;
oDbName : TFileName;
oDBDriverID : String;
oTableNames : TStringList;
public
end;
var
oFD_DB : TSW_FDDataBase;
procedure FD_DBTabellen_holen;
And here is the trouble maker:
procedure FD_DBTabellen_holen;
begin
oFD_DB.oFDConnection.GetTableNames('', '', '', oFD_DB.oTableNames,
[osMy, osOther], [tkTable], true);
end;

#Todd: thanks for answering very soon. I got 2 different errors starting with or without the debugger.
Without debugger: Runtime error 216 at 00408216
With debugger: An error occured in the project FD.exe reading from address 0x004739b7, read of address 0014
#fpiette:
TSW_FDDataBase = class
privat
oTableNames : TStringList;
public
end;
var oFD_DB : TSW_FDDataBase;
initialization
oFD_DB := TSW_FDDataBase.Create;
oFD_DB.Init;
end.

Related

Saving certain data into an array from a DBGrid

I would like to ask for help.
I have been tasked with saving FighterID into an array. This data will be used to make fighter 1 ,fight, fighter 2(the data has already been sorted in descending order according to wins they have).
I could only find ways to do this with 2D arrays, i would like tom use a 1 dimensional array.
I have no idea how to save a specific columns info into the array. I have tried the following and got the following error:
[dcc32 Error] Presets.pas(53): E2010 Incompatible types: 'string' and 'procedure, untyped pointer or untyped parameter'
Code:
procedure TPresetsForm.FormActivate(Sender: TObject);
var I:integer;
var s:string;
begin
qry1.Close;
qry1.sql.add('SELECT FighterName,Wins,Sponser FROM Preset_Fighters');
qry1.Active := true;
qry1.Open;
tbl111.Sort:= 'Wins DESC' ;
for I := 1 to 6 do
begin
Fightorder[I]:=(Presets.PresetsForm.dbgrd_info.Columns[0].FieldName:=('FighterID');
end;
end;
I have no idea if this is the correct way to obtain the FighterID example (MT54).
All data is from MS Access, I hope I have provided enough information
Thanks for the help XD
Please try the following code:
var
Fightorder: array[1..6] of string;
procedure TPresetsForm.FormActivate(Sender: TObject);
var
I: Integer;
begin
qry1.Close;
qry1.Sql.Add('SELECT FighterName, Wins, Sponser FROM Preset_Fighters');
qry1.Active := True;
qry1.Open;
for I := 1 to 6 do
begin
Fightorder[I] := qry1.Fields[0].AsString;
qry1.Next;
end;
end;
I think this will help you figure it out.

How to get the SQL content of the stored procedure in Delphi code?

I'm debugging Delphi code with Delphi 7 IDE, the code is not mine. My goal is to understand how this code works. This will help me to write my application which will interface with this code.
I never used Delphi in my life. I'm a C, C++, C# programmer.
I'm able to see the output (result) of the stored procedure but I need to see the content of the stored procedure (I mean the SQL statement). I looked in SQL Server side but I didn't find anything.
The stored procedure is called in this function :
function TDBDM.getID( prKeyName : string ) : integer;
begin
try
spGetSN.Parameters.ParamValues['#prKeyname'] := prKeyName;
spGetSN.open;
result := spGetSN.Fields[0].AsInteger;
finally
spGetSN.close;
end;
end;
and the stored procedure 'spGetSN' is declared here :
unit dmDB;
interface
uses
SysUtils, Classes, Forms, ADODB, DB, TypInfo, Dialogs, StdCtrls,
Controls,Registry, Windows;
type
eConnectionType = (ctSN);
TDBDM = class(TDataModule)
ADOConnectionSN: TADOConnection;
spGetSN: TADOStoredProc;
ADOQueryGetPartNoFromRMA: TADOQuery;
ADOQueryGetPartNoFromOrder: TADOQuery;
procedure ADOConnectionSNBeforeConnect(Sender: TObject);
procedure DataModuleCreate(Sender: TObject);
private
{ Private declarations }
function GetConnection( prConnectionType : eConnectionType ) :
TADOConnection;
public
{ Public declarations }
function getID( prKeyName : string ) : integer;
function getSN( prKeyName : string ) : string;
function getCommaValues( prSelect : string; prConnection :
eConnectionType = ctSN ) : string;
property pConnection[prType : eConnectionType] : TADOConnection read
GetConnection; default;
end;
var
DBDM: TDBDM;
implementation
uses dmRegistryClasses;
{$R *.dfm}
The text of the object TADOSTOREDPROC 'spGetSN' as defined in the IDE is :
object spGetSN: TADOStoredProc
Connection = ADOConnectionSN
ProcedureName = 'spGetSN;1'
Parameters = <
item
Name = '#RETURN_VALUE'
DataType = ftInteger
Direction = pdReturnValue
Precision = 10
Value = Null
end
item
Name = '#prKeyname'
Attributes = [paNullable]
DataType = ftWideString
Size = 30
Value = Null
end>
Left = 268
Top = 80
end
How can I print or watch the SQL code of this stored procedure in Delphi debugger? Thanks
The following should give you an idea how to do it:
Add a TAdoQuery called AdoQuerySP to your form and set its connection to whatever AdoConnection component you're using at the moment.
Add a TMemo to the form.
Add a TButton to the form and set its OnClick handler to execute the code below (after changing checklibuse to the name of your SP).
Code:
procedure TForm1.GetSPDefinition;
var
S : String;
begin
S := '';
AdoQuerySP.Sql.Text := 'exec sp_helptext checklibuse'; // sp_helptext
// is a Sql Server system stored proc to retrieve the definition of
// a stored proc. The definition is returned as a series of rows which need to be
// concatenated together as in the `while ...` loop below.
AdoQuerySP.Open;
while not AdoQuerySP.Eof do begin
if S <> '' then
S := #13#10 + S;
S := S + AdoQuerySP.FieldByName('Text').AsString;
AdoQuerySP.Next;
end;
// The retrieved definition is typically preceded by a number of blank lines
// which the call to Trim() remove.
Memo1.Lines.Text := Trim(S);
end;
You could query the sys.sql_modules table e.g.:
function MSSQL_GetModuleText(conn: TADOConnection; const ModuleName: WideString): WideString;
var
SQL: WideString;
rs: Variant;
begin
SQL := Format('SELECT [definition] FROM sys.sql_modules WHERE [object_id]=OBJECT_ID(N''%s'')', [ModuleName]);
rs := conn.Execute(SQL);
Result := VarToWideStr(rs.Fields[0]);
end;
Usage:
S := MSSQL_GetModuleText(ADOConnectionSN, 'dbo.spGetSN');

Delphi: What's wrong with REST API based on mORMot?

I try to build REST Web Server based on mORMot framework (Delphi) and have issue with getting data from remote DB.
Server.dpr
const
CONN_STR =
'DRIVER=SQL Server Native Client 10.0;UID=user;PWD=pass;server=server;'
+ 'Trusted_Connection=No;MARS_Connection=yes';
var
Model: TSQLModel;
RESTServer: TSQLRestServerDB;
Application: TwoStreamgateApplication;
HTTPServer: TSQLHttpServer;
ODBCConnProp: TSQLDBConnectionPropertiesThreadSafe;
begin
SQLite3Log.Family.Level := LOG_VERBOSE;
SQLite3Log.Family.PerThreadLog := ptIdentifiedInOnFile;
ODBCConnProp := TODBCConnectionProperties.Create('', CONN_STR, 'wo', 'wo');
try
ODBCConnProp.ThreadingMode := tmMainConnection;
Model := CreateModel;
VirtualTableExternalRegisterAll(Model, ODBCConnProp);
try
RESTServer := TSQLRestServerDB.Create(Model, ':memory:');
try
RESTServer.DB.Synchronous := smNormal;
RESTServer.DB.LockingMode := lmExclusive;
RESTServer.CreateMissingTables;
Application := TwoStreamgateApplication.Create;
try
Application.Start(RESTServer);
HTTPServer := TSQLHttpServer.Create('8092', [RESTServer]
{$IFNDEF ONLYUSEHTTPSOCKET}, '+', useHttpApiRegisteringURI{$ENDIF});
try
HTTPServer.RootRedirectToURI('api/default');
RESTServer.RootRedirectGet := 'api/default';
writeln('"MVC WOStreamgate Server" launched on port 8092 using ',
HTTPServer.HttpServer.ClassName);
writeln(#10'You can check http://localhost:8092/api/info for information');
writeln('or point to http://localhost:8092 to access the web app.');
writeln(#10'Press [Enter] to close the server.'#10);
readln;
writeln('HTTP server shutdown...');
finally
HTTPServer.Free;
end;
finally
Application.Free;
end;
finally
RESTServer.Free;
end;
finally
Model.Free;
end;
finally
ODBCConnProp.Free;
end;
Model is described below in ServerModel.pas unit
type
TSQLBaseData = class(TSQLRecord)
private
FName: RawUTF8;
FDetailURL: RawUTF8;
FLogoURL: RawUTF8;
FDescription: RawUTF8;
published
property Name: RawUTF8 read FName write FName;
property Description: RawUTF8 read FDescription write FDescription;
property LogoURL: RawUTF8 read FLogoURL write FLogoURL;
property DetailURL: RawUTF8 read FDetailURL write FDetailURL;
end;
TSQLStation = class(TSQLBaseData)
end;
function CreateModel: TSQLModel;
implementation
function CreateModel: TSQLModel;
begin
result := TSQLModel.Create(
[TSQLStation],
'api');
end;
View model in ServerViewModel.pas unit
TServerApplication = class(TMVCApplication, IServerApplication)
public
procedure Start(aServer: TSQLRestServer); reintroduce;
procedure StationView(ID: TID; out Station: TSQLStation);
end;
implementation
procedure TServerApplication.Start(aServer: TSQLRestServer);
begin
inherited Start(aServer,TypeInfo(IServerApplication));
fMainRunner := TMVCRunOnRestServer.Create(Self).
SetCache('Default',cacheRootIfNoSession,15);
aServer.Cache.SetCache(TSQLStation);
end;
procedure TServerApplication.StationView(ID: TID; out Station: TSQLStation);
begin
RestModel.Retrieve(ID, Station);
end;
and now once I type http://localhost:8092/api/station/10 in browser, I get response
{ "errorCode":404, "errorText":"Not Found" }
in case I type http://localhost:8092/api/stationview/10 in browser I get rendered page, but without info about station=10 or any other.
Where did I make a mistake? I want simply get JSON-response to my request
UPDATE:
Another info that can help. Once I try the query
procedure TestQuery();
var
SQLDBConnection: TODBCConnection;
Q: TQuery;
begin
SQLDBConnection := TODBCConnection.Create(ODBCConnProp);
Q := TQuery.Create(SQLDBConnection);
try
Q.SQL.Clear; // optional
Q.SQL.Add('select * from dbo.Station');
Q.Open;
...
I catch an error:
Project Server.exe raised exception class EODBCException with message 'TODBCStatement - TODBCLib
error: [HY106] [Microsoft][SQL Server Native Client 10.0]Fetch type
out of range (0) '.
Solution is very simple - need to add:
Database=myDataBase;
to
CONN_STR = 'DRIVER=SQL Server Native Client 10.0;UID=user;PWD=pass;server=server;'
+ 'Trusted_Connection=No;MARS_Connection=yes';Database=myDataBase;
Thanks Arnaud for great framework!!!

Login display error message

I need my program to log a user in from a database. This entails a diver number (like a username) and a password which is already in the database. Unfortunately, I don't know SQL right now and would rather use a technique similar to the one I've done here. I get an error message in run time that says: adotblDiversInfo: Cannot perform this operation on a closed dataset. Thank you so much for your help in advance (:
This is my code:
procedure TfrmHomeScreen.btnLogInClick(Sender: TObject);
var
iDiverNumber : Integer;
sPassword, sKnownPassword : String;
bFlagDiverNumber, bFlagPassword, Result : Boolean;
begin
iDiverNumber := StrToInt(ledDiverNumber.Text);
sPassword := ledPassword.Text;
with frmDM do
adotblDiversInfo.Filtered := False;
frmDM.adotblDiversInfo.Filter := 'Diver Number' + IntToStr(iDiverNumber);
frmDM.adotblDiversInfo.Filtered := True;
if frmDM.adotblDiversInfo.RecordCount = 0 then
ShowMessage(IntToStr(iDiverNumber) + ' cannot be found')
else
begin
sKnownPassword := frmDM.adotblDiversInfo['Password'];
if sKnownPassword = sPassword then
ShowMessage('Login successful')
else
ShowMessage('Incorrect password. Please try again');
end;
end;
The error you're getting is because you've forgotten to open the dataset before attempting to access it. Use frmDM.adoTblDiversInfo.Open; or frmDM.adoTblDiversInfo.Active := True; to do so before trying to use the table.
Your code could be much simpler (and faster) if you change it somewhat. Instead of filtering the entire dataset, simply see if you can Locate the proper record.
procedure TfrmHomeScreen.btnLogInClick(Sender: TObject);
var
iDiverNumber : Integer;
begin
if not frmDM.adoTblDiversInfo.Active then
frmDM.adoTblDiversInfo.Open;
iDiverNumber := StrToInt(ledDiverNumber.Text);
sPassword := ledPassword.Text;
if frmDM.adoTblDiversInfo.Locate('Diver Number', iDiverNumber, []) the
begin
if frmDM.adoTblDiversInfo['Password'] = ledPassword.Text then
ShowMessage('Login successful')
else
ShowMessage('Invalid password. Please try again.');
end
else
ShowMessage(ledDiverNumber.Text);
end;

Delphi stack overflow and access violation error when setting array (of records) length

I am busy building an application in which I am reading data from more two files of "records". I have a very strange error, which pops up depending on the sequence in which I open the files (see code below).
If I click button1 followed by button 2, thus calling the file of "weather data records" followed by the file of "parameters records", all is fine. If I do this the other way around, I get a "stack overflow" followed by "access violation at 0x7c90e898: write of address" error. This happens when I call SetLength for the array in Button1Click.
The weather data file has about 550 records, and the parameters file has about 45 records.
Can anyone see anything obvious wrong with my code? I am not sure how to attach files, or make them available, if anyone wants to use them to test...
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls, Grids,FileCtrl,Contnrs;
type
TWeatherData = record
MyDate : TDate;
Rainfall : Double;
Temperature : Double;
end;
TParameters = record
Species : string[50];
ParameterName: string[50];
ParameterValue : double;
end;
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
Var
WeatherDataFile : file of TWeatherData;
j : integer;
WeatherDataArray : array of TWeatherData;
MyFileSize : Integer;
begin
AssignFile(WeatherDataFile,'C:\Test5.cmbwthr') ;
Reset(WeatherDataFile);
MyFileSize := FileSize(WeatherDataFile);
SetLength(WeatherDataArray,MyFileSize);
j := 0;
try
while not Eof(WeatherDataFile) do begin
j := j + 1;
Read (WeatherDataFile, WeatherDataArray[j]) ;
end;
finally
CloseFile(WeatherDataFile) ;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
ParametersFile : file of TParameters;
j : integer;
CurrentParameters : array of TParameters;
MyFileSize : Integer;
begin
AssignFile(ParametersFile,'C:\Test5.cmbpara') ;
Reset(ParametersFile);
Reset(ParametersFile);
MyFileSize := FileSize(ParametersFile);
SetLength(CurrentParameters,MyFileSize);
j := 0;
try
while not Eof(ParametersFile) do begin
j := j + 1;
Read (ParametersFile, CurrentParameters[j]) ;
end;
finally
CloseFile(ParametersFile) ;
end;
end;
end.
You're writing past the ends of the arrays by incrementing the index before writing to the array instead of afterward. Since you're writing into memory that doesn't belong to the array, any number of problems may occur.
AssignFile(ParametersFile, 'C:\Test5.cmbpara');
Reset(ParametersFile);
try // Enter "try" block as soon as the file is opened.
MyFileSize := FileSize(ParametersFile);
SetLength(CurrentParameters, MyFileSize);
j := 0;
while not Eof(ParametersFile) do begin
Read(ParametersFile, CurrentParameters[j]);
Inc(j);
end;
finally
CloseFile(ParametersFile);
end;
if j <> MyFileSize then
raise Exception.CreateFmt('Parameter count mismatch: expected %d but got %d instead.',
[MyFileSize, j]);
You need packed records to save to a file.
type
TWeatherData = packed record
MyDate : TDate;
Rainfall : Double;
Temperature : Double;
end;
TParameters = packed record
Species : string[50];
ParameterName: string[50];
ParameterValue : double;
end;
Take a look at our TDynArray wrapper available in our SynCommons.pas unit. There is serialization feature included.
And you could put regular string inside the records, instead of shortstring: it will use less space on disk, and will be Unicode Ready since Delphi 2009.
type
TWeatherData = record
MyDate : TDate;
Rainfall : Double;
Temperature : Double;
end;
TWeatherDatas = array of TWeatherData;
TParameter = record
Species : string;
ParameterName: string;
ParameterValue : double;
end;
TParameters = array of TParameter;
var
Stream: TMemoryStream;
Params: TParameters;
Weather: TWeatherDatas;
begin
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile('C:\Test5.cmbpara');
DynArray(TypeInfo(TParameters),Params).LoadFromStream(Stream));
Stream.LoadFromFile('C:\Test5.cmbwthr');
DynArray(TypeInfo(TWeatherDatas),Weather).LoadFromStream(Stream));
finally
Stream.Free;
end;
end;
With TDynArray, you can access any dynamic array using TList-like properties and methods, e.g. Count, Add, Insert, Delete, Clear, IndexOf, Find, Sort and some new methods like LoadFromStream, SaveToStream, LoadFrom and SaveTo which allow fast binary serialization of any dynamic array, even containing strings or records - a CreateOrderedIndex method is also available to create individual index according to the dynamic array content. You can also serialize the array content into JSON, if you wish.
For Delphi 6 up to XE.

Resources