The following code is from the docs here:
Program ConnectDB
var AConnection : TSQLConnection;
Procedure CreateConnection;
begin
AConnection := TIBConnection.Create(nil);
AConnection.Hostname := 'localhost';
AConnection.DatabaseName := '/opt/firebird/examples/employee.fdb';
AConnection.UserName := 'sysdba';
AConnection.Password := 'masterkey';
end;
begin
CreateConnection;
AConnection.Open;
if Aconnection.Connected then
writeln('Succesful connect!')
else
writeln('This is not possible, because if the connection failed, ' +
'an exception should be raised, so this code would not ' +
'be executed');
AConnection.Close;
AConnection.Free;
end.
The main body of the code makes sense to me BUT I don't get where TSQLConnection came from. I cannot use CTRL + Space to autocomplete it either, which means my program has no reference to it. I'm trying to connect to Postgres by the way.
Can someone please state what TSQLConnection is? Thanks!
the TSQLConnection object is defined in the sqldb unit and is the base class for the specific connection components like the TIBConnection (interbase, firebird), TConnectionName (mysql) and TPQConnection (postgres).
Related
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 having a problem using the TFDDataSet component in my application.
I have a function that fetch many times if a customer has new orders. If it returns empty the function ends.
...
fdm_XMLREsumo.Close;
fdm_XMLREsumo.Active := false;
_DataSetJSON := SM.GetXMLResumoChave( pEnt_ID, pChave); //See Edit 1
_DataSet.Close;
_DataSet := TFDJSONDataSetsReader.GetListValueByName( _DataSetJSON, sXMLResumo );
_DataSet.Open; <-- here's the problem
if not _DataSet.IsEmpty then begin
exit;
end;
fdm_XMLREsumo.AppendData( _DataSet );
...
The problem is every time it executes _DataSet.Open; it creates a new connection in my DB.Because of that I'm having a too many connections exception.I've checked in my Server Properties and it is like this:
I have tried Connection.Close,_DataSet.Close, _DataSet.Free and _DataSet.Destroy but nothing worked.I read this, and it explains that even if you do _DataSet.Close the connection still exists, because DataSets work in memory.There is also this guy having a similar issue, but using Query. Does anyone know how can I manage to solve this?I am using MySQL
EDIT 1
As #CraigYoung helped me saying my example needs MCVE
SM.GetXMLResumoChave method:
Here it uses a connection to the database that is closed at the end of the function. Already debugged, and here it does not leave an open connection in MySQL Proccess List
function TDAOXMLResumo.GetXMLResumoChave(xEnt_id: Integer; xChave: String): TFDJSONDataSets;
begin
if not oSM.FDConn.Connected then
oSM.FDConn.Connected := true;
QueryPesquisa.SQL.Clear;
QueryPesquisa.SQL.Text :=
' select * from table' +
' where ent_id = :ent_id ' +
' and xre_chNFe = :xre_chNFe ';
QueryPesquisa.ParamByName('ent_id').Asinteger := xEnt_id;
QueryPesquisa.ParamByName('xre_chNFe').Asstring := xChave;
Result := TFDJSONDataSets.Create;
//TFDJSONDataSetsWriter.ListAdd Opens the Query that is passed as parameter and store the data as JSON in the TFDJSONDataSets (Result) object
TFDJSONDataSetsWriter.ListAdd(Result, sXMLResumo, QueryPesquisa);
//Closing the Query
QueryPesquisa.Close;
//Closing the Connection
oSM.FDConn.Close;
end;`
Basically, the _DataSet is only receiving a JSON List here: _DataSet := TFDJSONDataSetsReader.GetListValueByName( _DataSetJSON, sXMLResumo );, and then open it to access the data in it.
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.
This one looks like a bizarre one.
I have a Pascal unit that connects to a MySQL database
unit u_MySQLConnection;
interface
uses
ADODB,
AnsiStrings,
Generics.Collections,
SysUtils,
DB
;
type
TMySQLConnection = class
strict private
mysqlCon : TADOConnection;
public
function Connect:boolean;
destructor Destroy;
end;
var
MySQLConnection : TMySQLConnection;
implementation
function TMySQLConnection.Connect:boolean;
var
success : boolean;
begin
success := true;
try
if NOT (mysqlCon = nil)
then mysqlCon.Destroy;
mysqlCon := TADOConnection.Create(nil);
mysqlCon.ConnectionString := 'DRIVER={MySQL ODBC 3.51 Driver}; SERVER=localhost; DATABASE=database; UID=root; PASSWORD=password;OPTION=3;';
except
success := false;
end;
Result := success;
end;
destructor TMySQLConnection.Destroy;
begin
FreeAndNil(mysqlCon);
inherited;
end;
end.
And when I try to connect
MySQLConnection := TMySQLConnection.Create;
try
MySQLConnection.Connect;
finally
MySQLConnection.Destroy;
end;
I get a password prompt dialog box appearing, even though the password is already in the connection string. If I enter the username and password into this prompt, everything else works fine.
Things get a little bit more strange here:
When i move the database connection command into the main .dpr file as shown
program DieselBatch;
uses
Vcl.Forms,
u_MySQLConnection in '..\src\u_MySQLConnection.pas'
(*,
frm_About in '..\src\frm_About.pas' {frmAbout},
frm_AnalystDetails in '..\src\frm_AnalystDetails.pas' {frmAnalystDetails},
frm_Batch in '..\src\frm_Batch.pas' {frmBatch},
frm_ConfirmResultsChanged in '..\src\frm_ConfirmResultsChanged.pas' {frmConfirmResultsChanged},
frm_DebugSample in '..\src\frm_DebugSample.pas' {frmDebugSample},
frm_FlashManualEntry in '..\src\frm_FlashManualEntry.pas' {frmFlashEntry},
frm_Main in '..\src\frm_Main.pas' {frmMain},
frm_SampleComment in '..\src\frm_SampleComment.pas' {frmSampleComment},
frm_SelectAnalystForResult in '..\src\frm_SelectAnalystForResult.pas' {frmSelectAnalystForResult},
u_Data in '..\src\u_Data.pas',
u_MicroCheck in '..\src\u_MicroCheck.pas',
u_Undo in '..\src\u_Undo.pas'
*)
;
{$R *.res}
var
MySQLConnection : TMySQLConnection;
begin
MySQLConnection := TMySQLConnection.Create;
try
MySQLConnection.Connect;
finally
MySQLConnection.Destroy;
end;
Then the password prompt does not appear, as long as those units are commented out.
When I uncomment the above units again, the problem reappears.
Some of those units do use ADODB and DB but I can't see how the mere presence of the units should affect the behavour of the MySQLConnection unit ....
Short answer
Set the LoginPrompt property of the connection object to False.
Why it does not appear when you comment all the form units?
Well, the common DoConnect method of the TCustomConnection descendants checks the LoginPrompt property and then calls the LoginDialogProc/LoginDialogExProc procedural variable if assigned. The variables are declared in Data.DB.pas.
The VCL itself assigns this variables in the initialization section of the VCL.DBLogDlg.pas unit, which contains the standard dialog box you see, and this unit is used by the DBCtrls.pas unit, and automatically added to your project when you use any data-aware control.
If you comment out all the units containing data aware controls, the DBCtrls.pas unit is not linked in your executable and, as a consequence there's no registered login dialog to show when you're connecting.
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.