unit QueryThread;
interface
uses
System.Classes,
System.SysUtils,
Winapi.Windows,
Winapi.Messages,
UniProvider,
MySQLUniProvider,
Data.DB,
DBAccess,
Uni,
MemDS,
MemData,
VirtualTable;
// messages to be send to the main thread
const
TS_THREAD_STATUS_AVAILABLE = WM_USER + 1;
TS_THREAD_QUERY_STARTED = WM_USER +2;
TS_THREAD_QUERY_DONE = WM_USER +3;
TS_THREAD_QUERY_COUNT = WM_USER +4;
type
TThreadStatusMsg =
record
MessageStr:
string;
SQLStr:
string;
ExecTime:
String;
end;
PThreadStatusdMsg = ^TThreadStatusMsg;
TThreadQueryStartMsg =
record
Running: Boolean;
end;
PThreadQueryStartMsg = ^TThreadQueryStartMsg;
TThreadQueryDoneMsg =
record
Done: Boolean;
end;
PThreadQueryDoneMsg = ^TThreadQueryDoneMsg;
TThreadQueryRecCountMsg =
record
ICount: Int64;
end;
PThreadQueryRecCountMsg = ^TThreadQueryRecCountMsg;
type
TSqlQueryThrd =
class(TThread)
protected
procedure Execute;
override;
procedure OnAfterFetchEvent(DataSet: TCustomDADataSet);
procedure OnAfterOpen(DataSet: TDataSet);
private
FMainHandle: THandle;
FSQLText:
string;
fStatusText:
string;
FUniDacConnection: TUniConnection;
FUniDacSQLQuery: TUniQuery;
FVirtualTable: TVirtualTable;
ThreadStatusMsgPtr: PThreadStatusdMsg;
ThreadQueryStartedMsgPtr: PThreadQueryStartMsg;
ThreadQueryDoneMsgPtr: PThreadQueryDoneMsg;
ThreadQueryRecCountMsgPtr: PThreadQueryRecCountMsg;
function GetLongTime(aTime: TDatetime) :
string;
public
constructor Create(aMainHandle: THandle; aConnectionStr:
string;
aSQL:
string; aVTable: TVirtualTable);
overload;
destructor Destroy;
override;
end;
implementation
{ TSqlQueryThrd }
constructor TSqlQueryThrd.Create(aMainHandle: THandle;
aConnectionStr, aSQL:
string; aVTable: TVirtualTable);
begin
inherited Create(True);
// main thread handle
FMainHandle := aMainHandle;
FUniDacConnection := TUniConnection.Create(
nil);
FUniDacConnection.ProviderName := '
MySQL';
FUniDacConnection.ConnectString := aConnectionStr;
Assert(aConnectionStr <> '
', '
Connection-String can not be empty!');
// test if connection is successful otherwise goodbye
try
try
FUniDacConnection.Connect;
except
terminate;
end;
finally
// can be disconnected the unidac query established a connection by itself if needed
FUniDacConnection.Disconnect;
end;
FSQLText := aSQL;
Assert(FSQLText <> '
', '
SQL-Text can not be empty!');
FUniDacSQLQuery := TUniQuery.Create(
nil);
FUniDacSQLQuery.Connection := FUniDacConnection;
FUniDacSQLQuery.FetchingAll;
FUniDacSQLQuery.AfterFetch := OnAfterFetchEvent;
FUniDacSQLQuery.AfterOpen := OnAfterOpen;
FUniDacSQLQuery.SQL.Clear;
FVirtualTable := aVTable;
end;
destructor TSqlQueryThrd.Destroy;
begin
if FUniDacConnection.Connected
then
FUniDacConnection.Disconnect;
FUniDacConnection.Free;
FUniDacSQLQuery.Free;
inherited Destroy;
end;
procedure TSqlQueryThrd.Execute;
begin
NameThreadForDebugging('
StormThread');
FUniDacSQLQuery.SQL.Text := FSQLText;
// send the main thread some messages...
New(ThreadQueryStartedMsgPtr);
ThreadQueryStartedMsgPtr^.Running := True;
if not(PostMessage(FMainHandle, TS_THREAD_QUERY_STARTED,
integer(ThreadQueryStartedMsgPtr), 0))
then
begin
Dispose(ThreadQueryStartedMsgPtr);
terminate;
end;
New(ThreadStatusMsgPtr);
ThreadStatusMsgPtr^.MessageStr := '
Status: Query started';
ThreadStatusMsgPtr^.ExecTime := GetLongTime(Now);
ThreadStatusMsgPtr^.SQLStr := '
';
if not(PostMessage(FMainHandle, TS_THREAD_STATUS_AVAILABLE,
integer(ThreadStatusMsgPtr), 0))
then
begin
Dispose(ThreadStatusMsgPtr);
terminate;
end;
try
try
FUniDacSQLQuery.Execute;
// Must be set here otherwise the virtualtable in the main thread
// does not contain any data!
FVirtualTable.Assign(FUniDacSQLQuery);
except
FUniDacSQLQuery.Close;
FUniDacConnection.Close;
terminate;
end;
finally
FUniDacSQLQuery.Close;
FUniDacConnection.Disconnect;
terminate;
end;
end;
function TSqlQueryThrd.GetLongTime(aTime: TDatetime):
string;
var
formattedDate :
string;
begin
Result :='
';
try
DateTimeToString(formattedDate, '
hh:nn:ss:zz', aTime);
finally
Result := FormattedDate;
end;
end;
// special for unidac if that event is raised the query is done as written in the docu...
procedure TSqlQueryThrd.OnAfterFetchEvent(DataSet: TCustomDADataSet);
begin
New(ThreadStatusMsgPtr);
ThreadStatusMsgPtr^.MessageStr := '
Status: Query done';
ThreadStatusMsgPtr^.ExecTime := GetLongTime(Now);
ThreadStatusMsgPtr^.SQLStr := '
// ' + FSQLText;
if not(PostMessage(FMainHandle, TS_THREAD_STATUS_AVAILABLE,
integer(ThreadStatusMsgPtr), 0))
then
begin
Dispose(ThreadStatusMsgPtr);
terminate;
end;
New(ThreadQueryDoneMsgPtr);
ThreadQueryDoneMsgPtr^.Done := True;
if not(PostMessage(FMainHandle, TS_THREAD_QUERY_DONE,
integer(ThreadQueryDoneMsgPtr), 0))
then
begin
Dispose(ThreadQueryDoneMsgPtr);
terminate;
end;
end;
// this event is raised when the record count is available
procedure TSqlQueryThrd.OnAfterOpen(DataSet: TDataSet);
begin
New(ThreadQueryRecCountMsgPtr);
ThreadQueryRecCountMsgPtr^.ICount := FUniDacSQLQuery.RecordCount;
if not(PostMessage(FMainHandle, TS_THREAD_QUERY_COUNT,
integer(ThreadQueryRecCountMsgPtr), 0))
then
begin
Dispose(ThreadQueryRecCountMsgPtr);
terminate;
end;
end;
end.