Delphi XE8 : Memory Leak with method datasnap server - json

I'm currently designing a Datasnap rest server with Delphi. But I have serious memory leaks.
For example, my method station
procedure TV1.station(ID: integer);
begin
GetInvocationMetadata().ResponseContent := Manager.xxxxxxAPI.GetObjectStation(ID);
GetInvocationMetadata().CloseSession := true;
end;
Which call this function :
function TSmmAPI.GetObjectStation( const ID: integer ) : string;
(...)
jsonObject := TJSONObject.Create;
stationSelected := xxxxxManager.WorkShops.GetStation( CNCHandle );
with StatesDB.QueryGetCurrentState( stationSelected.Handle ) do begin
if RecordCount <> 0 then begin
ConvertFileToPcom(stationSelected.Ini.FileName, Pcom);
jsonObject.AddPair( TJSONPair.Create('ID', inttostr(ID)));
jsonObject.AddPair( TJSONPair.Create('Name', FieldByName(sbStaStationField).AsString));
jsonObject.AddPair( TJSONPair.Create('Workshop', stationSelected.Shop.Name));
jsonObject.AddPair( TJSONPair.Create('Group', Pcom.Others.GroupName));
jsonObject.AddPair( TJSONPair.Create('CurrentRef', FieldByName(sbStaRefNameField).AsString));
jsonObject.AddPair( TJSONPair.Create('CurrentState', FieldByName(sbStaStateField).AsString));
jsonObject.AddPair( TJSONPair.Create('Job', FieldByName(sbStaOPNameField).AsString));
jsonObject.AddPair( TJSONPair.Create('Order',FieldByName(sbStaOFNameField).AsString));
//(...), I have 12 addpair.
Disconnect;
end;
Destroy;
end;// with StatesDB.QueryGetCurrentState
result := jsonobject.toString;
jsonObject.FreeInstance;
end;
You can see, I use the resultContent instead of result from a function because I don't want result: in my json response.
So with the report from ReportMemoryLeaksOnShutdown, I see that all my jsonObject and each jsonpair are not destroy !!!
Result leak memory report, 5501 request from my client application
LifeCycle from the server class : Session
I use DSRESTWebDispatcher, set in Session Cycle and Timout at 60000.
Someone have an explanation? Did I forget to do something?

You should call jsonObject.Free instead of jsonObject.FreeInstance
You should never call FreeInstance directly to release the object. It is part of internal allocation/deallocation mechanism. In Delphi destructors call FreeInstance automatically to deallocate object instance memory.
Proper ways to release object instances in Delphi are:
TObject.Free - calls object instance destructor if instance is not nil
TObject.DisposeOf - introduced with Delphi ARC mobile compilers and on dektop compilers it calls TObject.Free.
FreeAndNil(var Obj) - procedure that calls Free on passed object instance and nils that reference

Related

When connecting to Oracle DB I get External: SIGSEGV error

I'll say how I reproduce the problem on lazarus.
I have a form and a datamodule using zeos to enstablish a connection with a local oracle db.
The problem born when I put some code to interlocute with the db.
Here is an example:
OracleMng.ZQuery1.SQL.Clear;
That is exactly the line going in error.
Here is the full code of the form:
unit form1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, DBGrids, StdCtrls,
datamodule2;
type
{ TLogin }
TLogin = class(TForm)
Button1: TButton;
DBGrid1: TDBGrid;
procedure Button1Click(Sender: TObject);
private
public
end;
var
Login: TLogin;
implementation
{$R *.lfm}
{ TLogin }
procedure TLogin.Button1Click(Sender: TObject);
begin
OracleMng.ZQuery1.SQL.Clear;
end;
end.
Here is the code of the datamodule:
unit datamodule2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DB, ZConnection, ZDataset, ZSqlMonitor;
type
{ TOracleMng }
TOracleMng = class(TDataModule)
DataSource1: TDataSource;
ZConnection1: TZConnection;
ZQuery1: TZQuery;
private
public
end;
var
OracleMng: TOracleMng;
implementation
{$R *.lfm}
{ TOracleMng }
end.
I'm trying
if (OracleMng <> Nil) and (OracleMng.Zquery1 <> Nil) then OracleMng.ZQuery1.SQL.add('select * from help');
if (OracleMng <> Nil) and (OracleMng.Zquery1 <> Nil) then OracleMng.ZQuery1.ExecSQL;
dbgrid1.refresh;
I have no more errors but the DBGrid1 is not filled.
This is my project lpr file:
program project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, zcomponent, datamodule2, form1
{ you can add units after this };
{$R *.res}
begin
RequireDerivedFormResource:=True;
Application.Scaled:=True;
Application.Initialize;
Application.CreateForm(TLogin, Login);
Application.Run;
end.
The fact that the change I suggested in my comment, namely
if (OracleMng <> Nil) and (OracleMng.Zquery1 <> Nil) then
OracleMng.ZQuery1.SQL.Clear
evidently stopped you getting the SIGSEGV error suggests that your DataModule and
form are being created in the wrong order, i.e. form first. Check this out by going to
Project | View Source in the IDE. If you see something like
program MyProgram;
[...]
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TDataModule1, DataModule1);
Application.Run;
end.
they are in the wrong order, so swap the two CreateForm lines
Application.CreateForm(TDataModule1, DataModule1);
Application.CreateForm(TForm1, Form1);
With that change, you should no longer need the
if (OracleMng <> Nil) and (OracleMng.Zquery1 <> Nil) then`
Next thing: You seem to be confused about when to use
ZQuery1.ExecSQL
and
ZQuery1.Open
Open is intended for when the SQL statement you are using produces a result set, that is
a collection of records which can be viewed in a TDBGrid. The most usual way to do this
is to use a SELECT statement as in
ZQuery1.SQL.Text := 'select * from MyTable';
ZQuery1.Open;
ExecQuery is intended for use where your SQL statement performs some operation on the database
which does not involve SELECTing records. The most common SQL statements which need ExecSQL are
UPDATE
INSERT
DELETE
though there are others, for example statements which execute stored procedures on the SQL Server
(note that some stored procedures return result sets and so need Open, rather than ExecSQL).
Note that ExecSQL will clear out any records which are in the dataset (ZQuery1) so after
you need to do Open again using a suitable SQL statement
var
S : String;
begin
S := 'update MyTable set number = number +1 where id = 5';
ZQuery.SQL.Text := S;
ZQuery1.ExecSQL; // no records shown in DBGrid1 from here
S := 'select * from MyTable';
ZQuery.SQL.Text := S;
ZQuery1.Open; // records shown in DBGrid1 again
end;
Note that I do
S := 'select * from MyTable';
ZQuery.SQL.Text := S;
instead of
ZQuery1.SQL.Clear;
ZQuery1.SQL.Add('select * from myTable');
The reason for this is that it's much easier to see the whole SQL statement in the debugger by
inspecting the variable S than inspecting the ZQuery1.SQL.Text property and much easier to
see any syntax errors.
You should always Close a dataset that you've Opened once you have finished working with it as it ensures what the data on disk is up to date. if the last SQL operation was ExecSQL, you don't need to close the dataset.
If you set the query's Text property the way I do, with ZQuery1.SQL.Text, you don't need to uses Clear. In any case, it is only equivalent to doing ZQuery1.SQL.Text := '' and it does not affect the state of the dataset - it only does anything when you call ExecSQL or Open.

I am trying to understand how to populate a TIWDBGrid(IntraWeb component) with a TFDStoredProc(FireDac component)

Currently I have a form that captures login data, a form with a TIWDBGrid that is supposed to return any hosts that are associated with the user_id that is created when I login from my mysql database, and a shared data module.
Below is my code for my login page
unit login_unit;
interface
uses
Classes, SysUtils, IWAppForm, IWApplication, IWColor, IWTypes, IWCompButton,
IWCompLabel, Vcl.Controls, IWVCLBaseControl, IWBaseControl, IWBaseHTMLControl,
IWControl, IWCompEdit;
type
Tlogin_form = class(TIWAppForm)
enter_usermame_TIWEdit: TIWEdit;
enter_password_TIWEdit: TIWEdit;
Username: TIWLabel;
Password: TIWLabel;
login_TIWButton: TIWButton;
returned_user_id_TIWEdit: TIWEdit;
procedure login_TIWButtonClick(Sender: TObject);
public
end;
implementation
{$R *.dfm}
uses email_data, host_lookup_unit;
procedure Tlogin_form.login_TIWButtonClick(Sender: TObject);
var
host_lookup_form:Thost_lookup_Form;
begin
email_data_DataModule.Login_userProc.Prepare;
email_data_DataModule.Login_userProc.ParamByName('user_name_').Value := enter_usermame_TIWEdit.Text;
email_data_DataModule.Login_userProc.ParamByName('pass_word_').Value := enter_password_TIWEdit.Text;
email_data_DataModule.Login_userProc.Execute;
email_data_DataModule._user_id := email_data_DataModule.Login_userProc.ParamByName('user_id_').Value;
returned_user_id_TIWEdit.Text := email_data_DataModule.Login_userProc.ParamByName('user_id_').Value;
email_data_DataModule.Hosts_requested_frm_user_idProc.Prepare;
email_data_DataModule.Hosts_requested_frm_user_idProc.ParamByName('user_id_').Value := email_data_DataModule._user_id;
email_data_DataModule.Hosts_requested_frm_user_idProc.Execute;
thost_lookup_form.Create(Self).Show;
end;
initialization
Tlogin_form.SetAsMainForm;
end.
My host_look up page has a TIWDBGrid component and it looks like the following:
unit host_lookup_unit;
interface
uses
Classes, SysUtils, IWAppForm, IWApplication, IWColor, IWTypes, Vcl.Controls,
IWVCLBaseControl, IWBaseControl, IWBaseHTMLControl, IWControl, IWCompGrids,
IWDBGrids, IWCompButton;
type
Thost_lookup_Form = class(TIWAppForm)
IWDBGrid1: TIWDBGrid;
public
end;
implementation
{$R *.dfm}
uses email_data;
end.
I made sure that the DataSource option was set to email_data_DataModule.hosts_table_requested_TDataSource
And finally the code for my data module is as follows:
unit email_data;
interface
uses
Forms,
SysUtils, Classes, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error,
FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool,
FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.MySQL, FireDAC.Phys.MySQLDef,
FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf,
FireDAC.DApt, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client;
type
Temail_data_DataModule = class(TDataModule)
EmaildbConnection: TFDConnection;
Login_userProc: TFDStoredProc;
hosts_table_requested_TDataSource: TDataSource;
Hosts_requested_frm_user_idProc: TFDStoredProc;
private
public
_user_id : integer;
end;
function email_data_DataModule:Temail_data_DataModule;
implementation
{$R *.dfm}
uses ServerController;
function email_data_DataModule:Temail_data_DataModule;
begin
result := UserSession.email_data_DataModule;
end;
end.
How can I make sure to populate the data from the Hosts_requested_frm_user_idProc to my IWDBGrid?
Thanks in advance and sorry for the long post
There are a few issues with your application:
First, all your forms need to have WebApplication object as the owner, so change your line where you create your IWForm to
procedure Tlogin_form.login_TIWButtonClick(Sender: TObject);
var
host_lookup_form:Thost_lookup_Form;
begin
...
thost_lookup_form.Create(WebApplication).Show;
end;
Second, I think you shouldn't be using a stored proc to retrieve data, but a query object. If you are using FireDAC consider changing it to a TFDQuery which will retrieve a result set.
Also, you must put your TDataSource in the same form where the IWDBGrid is, and connect them via IWDBGrid.DataSource property.
Add the unit where the DataModule is declared to the uses clause of the IWForm (in the interface section), and declare a field of the form. So you should have something like this:
unit host_lookup_unit;
interface
uses
Classes, SysUtils, IWAppForm, IWApplication, IWColor, IWTypes, Vcl.Controls,
IWVCLBaseControl, IWBaseControl, IWBaseHTMLControl, IWControl, IWCompGrids,
IWDBGrids, IWCompButton,
email_data; // <- Include your DM here
type
Thost_lookup_Form = class(TIWAppForm)
IWDBGrid1: TIWDBGrid;
hosts_table_requested_TDataSource: TDataSource;
private
FDataModule: Temail_data_DataModule;
public
end;
implementation
{$R *.dfm}
procedure Thost_lookup_Form .host_lookup_FormCreate(Sender: TObject);
begin
FDataModule := email_data_DataModule; // set your field referencing the DM here and use it within your form. Don't use the email_data_DataModule() function anymore!
hosts_table_requested_TDataSource.DataSet := FDataModule.Login_userQuery; // Connect your DataSource and your DataSet, via code. This is the best way to do it!
end;
I also strongly suggest that you remove the logic that deals with setting parameters and opening the query from your form. This code belongs to the DataModule!
You should have something like this:
unit email_data;
interface
uses
Forms,
SysUtils, Classes, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error,
FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool,
FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.MySQL, FireDAC.Phys.MySQLDef,
FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf,
FireDAC.DApt, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client;
type
Temail_data_DataModule = class(TDataModule)
EmaildbConnection: TFDConnection;
Login_userQuery: TFDQuery;
Hosts_requested_frm_user_idQuery: TFDQuery;
private
F_user_id: Integer;
public
function SetUserNameAndPassword(const AUserName, AUserPassword: string): Integer;
end;
function email_data_DataModule:Temail_data_DataModule;
implementation
{$R *.dfm}
uses ServerController;
function email_data_DataModule: Temail_data_DataModule;
begin
result := UserSession.email_data_DataModule;
end;
function Temail_data_DataModule.SetUserNameAndPassword(const AUserName, AUserPassword: string): Integer;
begin
Login_userQuery.Prepare;
Login_userQuery.ParamByName('user_name_').Value := AUserName;
Login_userQuery.ParamByName('pass_word_').Value := AUserPassword;
Login_userQuery.Open;
F_user_id := Login_userQuery.ParamByName('user_id_').Value;
Result := Login_userQuery.ParamByName('user_id_').Value;
Hosts_requested_frm_user_idQuery.Prepare;
Hosts_requested_frm_user_idQuery.ParamByName('user_id_').Value := F_user_id;
Hosts_requested_frm_user_idQuery.Open;
end;
end.
all you need to do now is build and run the application. It should work.
Note: All code above is untested (I wrote it in Notepad, not in Delphi IDE) so it might have typos and some other errors.

Initializing ROM from array using functions, Synthesis ERROR (VHDL)

Ok, so I have a problem with a ROM initialization function.
Before I get into the problem let me explain a bit the nature of my problem and my code.
What I want to do is generate N number of ROMs which I have to use as an input for a module that runs a matching algorithm.
My problem is that I have a file with my signatures (let's say 64 in total) which I want to load in my different ROMs depending on how many I've generated (powers of 2 in my case, e.g. 8 roms of 8 signatures each).
I figured the best way to do it is load the whole text file into an array (using a function outside of the architecture body) which I will then use (again in a function) to load the data into a smaller array which will then be my ROM.
It seemed to me that the synthesizer would then just ignore the big array since I don't actually use it in my architecture.
Problem now is that since the second array input arguments are signal dependent, the synthesizer just ignores them and ties my array to zero (line 57).
Does anyone know how else if there's a way to make this architecture synthesize-able?
library ieee;
use ieee.std_logic_1164.all;
use IEEE.std_logic_signed.all;
use std.textio.all;
entity signatures_rom_partial is
generic(
data_width : integer := 160;
cycle_int : integer :=32;
rom_size : integer := 4
);
port ( clk : in std_logic;
reset : in std_logic;
readlne: in integer range 0 to cycle_int-1; -- user input for array data initialization
address: in integer range 0 to rom_size-1; -- address for data read
data: out std_logic_vector(data_width-1 downto 0) -- data output
);
end signatures_rom_partial;
architecture rom_arch of signatures_rom_partial is
type rom_type is array (0 to cycle_int-1) of bit_vector (data_width-1 downto 0); -- big array for all signatures, not used in arch
type test_type is array (0 to rom_size-1) of std_logic_vector (data_width-1 downto 0); -- smaller ROMs used in arch
--Read from file function--
----------------------------------------------------------------------------------------------------------
impure function InitRomFromFile (RomFileName : in string) return rom_type is --
file RomFile : text is in RomFileName; --
variable RomFileLine : line; --
variable rom : rom_type; --
--
begin --
for i in rom_type'range loop --
readline (RomFile, RomFileLine); --
read (RomFileLine, rom(i)); --
end loop; --
return rom; --
end function; --
----------------------------------------------------------------------------------------------------------
--Function for smaller ROM initialization--
----------------------------------------------------------------------------------------------------------
impure function initPartRom (rom : rom_type; readlne : integer) return test_type is --
variable test_array : test_type; --
--
begin --
for j in test_type'range loop --
test_array(j) := to_stdlogicvector(rom(j+readlne)); --
end loop; --
return test_array; --
end function; --
----------------------------------------------------------------------------------------------------------
constant rom : rom_type := InitRomFromFile("signatures_input.txt");
signal test_array : test_type := initPartRom(rom , readlne); --(LINE 57) SYNTHESIZER IGNORES THESE INPUT ARGUMENTS
begin
process(clk,reset)
begin
if reset='1' then
data<=(others=>'0');
elsif (clk'event and clk='1') then
data <= (test_array(address));
end if;
end process;
end rom_arch;
Synthesis is done using Xilinx ISE, simulation is done with Modelsim in which, my creation works fine :)
Thanks for any help!
With Xilinx ISE (14.7) it is possible to synthesize ROMs and RAMs even if the initial data is read from an external file by function.
The only requirement is, that the reading function must be computable at synthesis time. This is not true for your code because readlne is not static at the point the function is called. You should change it to a generic instead of an input and assign a different value to this generic for every other ROM instance. Then it should work as intended.
A sample implementation of how to read initialization data from a text file in (Xilinx) .mem format can be found in VHDL Library PoC in the namespace PoC.mem.ocrom or PoC.mem.ocram

Refresh Queries in Threads

I`m using Delphi XE6 and UniDAC and MySQL
I have some TUniQuery components in my DM and I want to Refresh theme repeatedly, so I put some Timers in my main form and in each timer I create a thread and pass a query to it for refreshing data :
for Example :
TUpdateThread = class(TThread)
private
FQuery : TUniQuery;
FResultHandle : THandle;
public
constructor Create(var Query : TUniQuery; ResultHandle : THandle);
protected
procedure Execute; override;
end;
constructor TUpdateThread.Create(var Query: TUniQuery; ResultHandle : THandle);
begin
inherited Create;
Suspend;
FQuery := Query;
FResultHandle := ResultHandle;
FreeOnTerminate := True;
Resume;
end;
procedure TUpdateThread.Execute;
var
Msg : String;
B : Boolean;
begin
try
B := True;
try
FQuery.Refresh;
except
on E:Exception do
begin
B := False;
Msg := 'Error : ' + #13 + E.Message;
SendMessage(FResultHandle, MSG_UPDATERESULT, 2, Integer(Msg));
end;
end;
finally
if B = True then
SendMessage(FResultHandle, MSG_UPDATERESULT, 1, 1);
Terminate;
end;
end;
Sometimes it`s done successfully but many times I got some errors such as AVs or "Net Pack Header ... " error
or sometimes I have problem in my Grids ( Ehlib DBGrid ) such as error in drawing rows or ... ( specially when I use DisableControls and EnableControls )
All of Queries have same connection , I think each Thread should have his own connection, because of all timers intervals are same , I suggest sometimes refreshing queries interrupts each others
In fact, my database is in a VPS server and there is some client applications , I want to have Live-Tables in Clients and update theme repeatedly
What is the best way to achieve that ?
how I should update my Tables without application hangs !
there is some components as TThreadTimer ( or ... ) , is theme useful for this situation ?!
thanks ...
The first issue is here :
constructor TUpdateThread.Create(var Query: TUniQuery; ResultHandle : THandle);
begin
inherited Create; // Create with no arguments
Suspend; // means CreateSuspended = false
FQuery := Query;
FResultHandle := ResultHandle;
FreeOnTerminate := True;
Resume;
end;
Here you create the thread with the default constructor (CreateSuspended = false) where the thread begins running immediately. You call suspend (which is deprecated and should not be used) immediately, but this is still a race condition since your thread may or may not start trying to Refresh your query before you've assigned it. To create the thread in a suspended state use the overload constructor of
inherited Create(true);
Resume is also deprecated. Instead you should use Start;.
Further, you're passing in a TUniQuery to this thread's constructor. We can assume, I imagine, that this query has affinity to the main thread - this is to say that it is (perhaps) a visual component on a form, has databindings to visual components, or is otherwise interacted with by the user or user interface.
The answer, if so, is that you simply cannot do this - a thread cannot modify an object with affinity to another thread. Your interface may be in the middle of retrieving records from the query when the background thread, for example, is simultaneously destroying them in preparation to refresh the query contents. Naturally this will cause all sorts of problems.
The simple solution is to use a regular timer and refresh synchronously on the main thread. If this takes too long then you need to consider a different strategy altogether. We don't really have sufficient information to suggest much further. If network access and I/O is the bottleneck then you might consider asynchronously refreshing to a separate query object owned by the thread, then synchronously assign it to your view components.

Querying MYSQL from an external application (is my code inefficient)?

I have a database that I need to query over and over as fast as possible. My queries execute pretty quickly, but there seems to be some additional lag.
I have a feeling that this lag is due to the fact that I am initiating and de-initiating a connection the connection each time. Is there a way to avoid this?
I am not using libmysql (at least, not directly). I am using the "mysql50" package in Lazarus/FreePascal (similar to delphi), which in turn uses libmysql ( I think ).
I would really appreciate if someone took a look at my code and pointed out (or maybe even fixed ) some inefficiencies.
The purpose of this library is to pass along a query sent from MQL4 (a propitiatory C-like language for the financial exchange market), and return a single row from my MYSQL database (to which it connects through a pipe).
{$CALLING STDCALL}
library D1Query;
{$mode objfpc}{$H+}
uses
cmem,
Windows,
SysUtils,
profs_win32exceptiontrap,
mysql50;
var
sock: PMYSQL;
qmysql: st_mysql;
type
VArray = array[0..100] of Double;
PArray = ^VArray;
procedure InitSQL; stdcall;
begin
mysql_init(PMySQL(#qmysql));
sock :=
mysql_real_connect(PMysql(#qmysql), '.', 'root', 'password', 'data', 3306, 'mysql', CLIENT_MULTI_STATEMENTS);
if sock = nil then
begin
OutputDebugString(PChar(' Couldn''t connect to MySQL.'));
OutputDebugString(PChar(mysql_error(#qmysql)));
halt(1);
end;
end;
procedure DeInitSQL; stdcall;
begin
mysql_close(sock);
end;
function SQL_Query(QRY: PChar; output: PArray): integer; stdcall;
var
rowbuf: MYSQL_ROW;
recbuf: PMYSQL_RES;
i: integer;
nfields: LongWord;
begin
InitSQL();
if (mysql_query(sock, QRY) < 0) then
begin
OutputDebugString(PChar(' Query failed '));
OutputDebugString(PChar(' ' + mysql_error(sock)));
end;
recbuf := mysql_store_result(sock);
nfields := mysql_num_fields(recbuf);
rowbuf := mysql_fetch_row(recbuf);
if (rowbuf <> nil) then
begin
for i:=0 to nfields-1 do
output^[i] := StrToFloatDef(rowbuf[i], -666);
end;
mysql_free_result(recbuf);
DeInitSQL();
Result := i;
end;
exports
SQL_Query,
InitSQL,
DeInitSQL;
begin
end.
You could use Initialization and Finalization blocks to handle setting up and tearing down the SQL connection. That way you remove the overhead of connection setup from each query that you execute. You can find more info on Initialization and Finalization here.
From the link:
The initialization block is used to initialize certain variables or execute code that is necessary for the correct functioning of the unit. The initialization parts of the units are executed in the order that the compiler loaded the units when compiling a program. They are executed before the first statement of the program is executed.
The finalization part of the units are executed in the reverse order of the initialization execution. They are used for instance to clean up any resources allocated in the initialization part of the unit, or during the lifetime of the program. The finalization part is always executed in the case of a normal program termination: whether it is because the final end is reached in the program code or because a Halt instruction was executed somewhere.