ZEOSLIB increasing memory usage inside TThread - freepascal

I tried to run a SELECT SQL to retrieve some data from MySql database. For this, I had created a TZQuery inside a TThreadn to avoid freezing the GUI of the application.
But, after the end of each consult (tested with 100,000+ rows, just to "force" a freeze) the memory was increasing instead of returning to the initial values (even after FreeAndNil(ZQuery) and FreeAndNil(TThread)) increasing ~2mb to each query executed (verified using Windows taskman.)
The test:
1 - Click button1 to start thread and query
2 - Wait callback
3 - Click button2 to finish and free query
{ TForm }
procedure TForm1.Button1Click(Sender: TObject);
begin
//DBQuery: TSubThreadTest;
DBQuery:= TSubThreadTest.Create(SQLStatement, true, ZCon, #onQueryCallBack);
DBQuery.Start;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
FreeAndNil(DBQuery);
end;
procedure TForm1.onQueryCallBack(fail: boolean; query: TZQuery);
begin
lLastExec.Caption:= FormatDateTime('hh:mm:ss', Now);
end;
{ TSubThreadTest }
constructor TSubThreadTest.Create(SQLStatement: string; toExec: boolean;
connector: TZConnection; EOnConclude: TSubThreadTestCallBack);
begin
inherited Create(true);
//main: TZQuery;
main:= TZQuery.Create(nil);
main.Connection:= connector;
main.SQL.Text:= SQLStatement;
isToExec:= toExec;
OnConclude:= EOnConclude;
FreeOnTerminate:= False;
end;
procedure TSubThreadTest.callback;
begin
if assigned(OnConclude) then
OnConclude(true, main);
end;
procedure TSubThreadTest.Execute;
begin
if isToExec then
main.ExecSQL
else
main.Open;
Synchronize(#callback);
FreeAndNil(main);
end;
First thread create and call: 24mb memory usage
Second thread create and call: 26,5mb memory usage
Etc...

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.

Odd exception behavior in Inno script when CurPageChanged is declared

In the below script, an AfterInstall procedure (SetupOperation) executes after the installation of the lone file installed. If it fails, I want to abort setup, which I do by calling WizardForm.Close. The problem is, if the CurPageChanged procedure is merely declared, the the exception is never properly rethrown in SetupOperation. If I comment it out, everything works properly. There's got to be something simple I'm missing.
[Setup]
DisableDirPage=yes
DisableProgramGroupPage=yes
AppID=someuniqueid
DefaultDirName={pf}\MyCompany\ExampleApp
SetupLogging=yes
AppName=ExampleApp
AppVersion=1.2.3.4
AppVerName=ExampleApp 1.2.3.4
AppPublisher=MyCompany
OutputBaseFilename=MyCompany.ExampleApp.1234
[Files]
Source: "d:\temp\test.txt"; DestDir: "{commonappdata}\MyCompany\ExampleApp"; AfterInstall: SetupOperation
[Code]
var SharedProgressPage: TOutputProgressWizardPage;
// Called by Inno to initialize the user interface
procedure InitializeWizard();
begin
Log('InitializeWizard called');
SharedProgressPage := CreateOutputProgressPage('Installing', 'Please wait while Setup installs the ExampleApp on your computer.');
SharedProgressPage.SetProgress(0,1);
SharedProgressPage.ProgressBar.Style := npbstMarquee;
end;
// Called when the current wizard page changes
procedure CurPageChanged(CurPageID: Integer);
begin
end;
procedure SetupOperation();
begin
Log('SetupOperation called');
try
SharedProgressPage.SetText('', '');
SharedProgressPage.Show;
try
SharedProgressPage.SetText('Performing operation...', '');
RaiseException('Some weird error');
except
Log('Caught an exception; re-raising');
RaiseException(GetExceptionMessage());
finally
Log('Closing progress page');
SharedProgressPage.Hide;
end;
except
Log('An error occurred setting performing the operation: ' + GetExceptionMessage());
WizardForm.Close();
end;
end;
This might have something to do with the usage of a TOutputProgressWizardPage, but I don't see how. It seems to get shown/hidden properly.

Delphi 10, Digital Persona and MySQL

I am at my wits end at the moment. I have looked everywhere for a way of verifying a captured fingerprint in my MySQL database using the Digital Persona SDK(One Touch) and Delphi 10. I am able to save the fingerprint as a longblob in my database but I am unable to verify from my database. Hopefully someone here would be able to assist me. Reader is a U.Are.U 4500.
Below is the code I use to save the fingerprint, but I have no idea how to query the database when I need to verify the fingerprint again.
procedure TForm1.FPCaptureComplete(ASender: TObject;
const ReaderSerNum: WideString; const pSample: IDispatch);
var
l_interface : IDispatch;
outFile : File;
vt : integer ;
vtByteBuf : PByteArray;
aryLow : integer;
aryHigh : integer;
rawDataSize: integer;
loopIndex : integer;
begin
l_interface := FPGetImage.ConvertToPicture(pSample);
lblInfo.Caption:='Sample Captured';
SetOlePicture(pbPrint.Picture,IPictureDisp(l_interface)); //display print
if breginprogress=true then begin
if index > 4 then index:=1; //reset index if beyond 4 presses
if index=1 then
begin
lbl1.Font.Color:=clGreen;
lbl2.Font.Color:=clYellow;
end;
if index=2 then
begin
lbl2.Font.Color:=clGreen;
lbl3.Font.Color:=clYellow;
end;
if index=3 then
begin
lbl3.Font.Color:=clGreen;
lbl4.Font.Color:=clYellow;
end;
if index=4 then lbl4.Font.Color:=clGreen;
index := index + 1;
//Create registration\enrollment featureset from sample captured
try
FPExtraction.CreateFeatureSet(pSample,DataPurposeEnrollment);
except
on E: Exception do begin
showmessage('Exception inside CreateFeatureSet');
showmessage(E.Message);
FPregister.Clear;
ResetLabels;
index:=1;
exit;
end;
end;
if FPExtraction.FeatureSet <> nil then
//Add features to registration object
FPRegister.AddFeatures(FPExtraction.FeatureSet)
else begin
Showmessage('Could not create featureset, poor press');
FPRegister.Clear;
ResetLabels;
index:=1;
exit; //return
end;
//If 4 successful features added, status should be 'Ready'
if FPRegister.TemplateStatus=TemplateStatusTemplateReady then begin
lblResult.Caption:='User Enrolled - Press Finger for Verification';
lbl1.Visible:=false; lbl2.Visible:=false; lbl3.Visible:=false; lbl4.Visible:=false;
FPTemplate:=FPRegister.Template as DPFPShrXLib_TLB.DPFPTemplate;
breginprogress:=false; //stop registration process, enable verification
//Before saving data to database you will need to get the raw data (variant)
try
vrnt:=FPTemplate.Serialize; //raw data is now stored in this variant
aryLow:=VarArrayLowBound(vrnt,1);
aryHigh:=varArrayHighBound(vrnt,1);
aryHigh:=aryHigh-aryLow;
vtByteBuf:=VarArrayLock(vrnt); //lock down the array
for loopIndex := 0 to aryHigh - 1 do
fpData[loopIndex]:=vtByteBuf[loopIndex];
VarArrayUnlock(vrnt);
//Save fpData to database here
//Database logic is not provided here. Plenty examples on web on
//How to save a byte array (binary data) to database.
SaveFP;
except
on E: Exception do showmessage('Trouble saving data');
end;
end;
end;
end;
//This is the pysical save
procedure TForm1.SaveFP;
var
tptStream: TMemoryStream;
p: Pointer;
begin
MemberTbl.Insert;
MemberTbl.FieldByName('MemberName').AsString := NameEdit.Text;
tptStream := TMemoryStream.Create();
tptStream.Position := 0;
p := VarArrayLock(vrnt);
tptStream.Write(p^, VarArrayHighBound(vrnt, 1));
VarArrayUnlock(vrnt);
(MemberTbl.FieldByName('MemberFP') as BlobField).LoadFromStream(tptStream);
MemberTbl.Post;
tptStream.Free();
end;
I've created a component wrapper for the DigitalPersona One Touch for Windows SDK Version 1.6.1 (August 2010)
I've tested it with the DigitalPersona U.are.U 4000B reader, but according to the documentation, it should work with the DigitalPersona U.are.U 4500 reader too
You can have a look and download the component here
Then you can add code like this on the OnCaptured event handler:
procedure TForm1.DPFingerPrintReader1Captured(Reader: TDPFingerPrintReader; FingerComparer: IFingerComparer);
var
LFingerPrintField: TField;
begin
LFingerPrintField := YourDataSet.FieldByName('FingerPrintField');
while not YourDataSet.Eof do
begin
if FingerComparer.CompareTo(LFingerPrintField.AsString) then
begin
ShowMessage('Found');
Exit;
end;
YourDataSet.Next;
end;
ShowMessage('NOT Found');
end;

How to call a procedure from inside another procedure

There is this link How do I Invoke a procedure when inside another procedure in Pascal But its not exactly my case.
procedure TForm1.Button1Click(Sender: TObject);
var
[...]
begin
// click on button
[...]
end;
and I have this procedure
procedure TForm1.CheckListBox2DblClick(Sender: TObject);
begin
// on double click in flags
[the same code like above]
end;
i tryed this but it does not work
procedure TForm1.CheckListBox2DblClick(Sender: TObject);
begin
TForm1.Button1Click;
end;
then I tryed this
procedure TForm1.CheckListBox2DblClick(Sender: TObject);
begin
TForm1.Button1Click(Sender: TObject);
end;
it also does not work
Can somebody please help me ?
Just call it directly, using either nil or another component as the Sender:
procedure TForm1.CheckListBox2DblClick(Sender: TObject);
begin
Button1Click(nil);
end;
procedure TForm1.CheckListBox2DblClick(Sender: TObject);
begin
Button1Click(CheckListBox2);
end;
Note you don't use the classname (or variable name) of the form itself, since you're calling from the current instance of the form. IOW, do not use TForm1 or Form1 inside of a class method; that limits you to a specific instance of the form instead of being available to all instances. If you need to qualify it, use Self, as in Self.Button1Click(nil);.
Try this
procedure TForm1.CheckListBox2DblClick(Sender: TObject);
begin
TForm1.Button1Click(Sender);
end;

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.