Page 1 of 1
Access violation after a Mysql disconnection
Posted: Mon 18 Jun 2012 09:06
by zeltron73
Hello,
This code produces an access violation when the second query is freed.
Code: Select all
procedure TForm1.Button1Click(Sender: TObject);
var
sei: TShellExecuteInfo;
begin
TButton(Sender).Enabled := False;
Query1.ParamByName('PrimaryKey').AsInteger := 1;
Query1.Prepare;
Query1.Open;
Query2.ParamByName('PrimaryKey').AsInteger := 1;
Query2.Prepare;
Query2.Open;
try
// stops the Mysql service and wait
FillChar(sei, SizeOf(ShellExecuteInfo), 0);
sei.cbSize := SizeOf(ShellExecuteInfo);
sei.Wnd := Handle;
sei.fMask := SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb := 'open';
sei.lpFile := 'NET.EXE';
sei.lpParameters := 'STOP MYSQL'; // NET = synchronous command (unlike SC)
sei.lpDirectory := PChar(GetCurrentDir);
if ShellExecuteEx(@sei) then
begin
while (MsgWaitForMultipleObjects(1, sei.hProcess, False, 500, QS_ALLINPUT) <> WAIT_OBJECT_0) do
Application.ProcessMessages;
CloseHandle(sei.hProcess);
// starts the Mysql service and wait
sei.lpParameters := 'START MYSQL';
if ShellExecuteEx(@sei) then
begin
while (MsgWaitForMultipleObjects(1, sei.hProcess, False, 500, QS_ALLINPUT) <> WAIT_OBJECT_0) do
Application.ProcessMessages;
CloseHandle(sei.hProcess);
end;
end
else
raise Exception.Create('Unable to restart MySQL service');
// queries have been closed
Query1.Open;
Query2.Open;
finally
TButton(Sender).Enabled := True;
try Connection.Free except end; // raises an EMysqlConnection ('Lost connection')
Query2.Free; // OK
Query1.Free; // Access Violation
end;
end;
By design, I cannot use a disconnected mode. So I have to take into account a potential loss of connection... but I get access violations when the tables are released.
Thanks for your help.
Re: Access violation after a Mysql disconnection
Posted: Tue 19 Jun 2012 08:06
by zeltron73
Hello,
If you execute this code, you can watch an assertion that failed with this message: EAssertionFailed (MyClasses.pas) line 4724
Code: Select all
procedure TForm1.Button1Click(Sender: TObject);
const
QUERY_1 = 'SELECT t1.* FROM `Table1` t1 WHERE t1.PrimaryKey = :Key';
QUERY_2 = 'SELECT t2.* FROM `Table2` t2 WHERE t2.PrimaryKey = :Key';
var
sei: TShellExecuteInfo;
begin
TButton(Sender).Enabled := False;
Query1.SQL.Text := QUERY_1;
Query1.ParamByName('Key').AsInteger := 1;
Query1.Prepare;
Query1.Open;
Query2.SQL.Text := QUERY_2;
Query2.ParamByName('Key').AsInteger := 1;
Query2.Prepare;
Query2.Open;
try
// stops the Mysql service and wait
FillChar(sei, SizeOf(ShellExecuteInfo), 0);
sei.cbSize := SizeOf(ShellExecuteInfo);
sei.Wnd := Handle;
sei.fMask := SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb := 'open';
sei.lpFile := 'NET.EXE';
sei.lpParameters := 'STOP MYSQL'; // NET = synchronous command (unlike SC)
sei.lpDirectory := PChar(GetCurrentDir);
if ShellExecuteEx(@sei) then
begin
while (MsgWaitForMultipleObjects(1, sei.hProcess, False, 500, QS_ALLINPUT) <> WAIT_OBJECT_0) do
Application.ProcessMessages;
CloseHandle(sei.hProcess);
// starts the Mysql service and wait
sei.lpParameters := 'START MYSQL';
if ShellExecuteEx(@sei) then
begin
while (MsgWaitForMultipleObjects(1, sei.hProcess, False, 500, QS_ALLINPUT) <> WAIT_OBJECT_0) do
Application.ProcessMessages;
CloseHandle(sei.hProcess);
end;
end
else
raise Exception.Create('Unable to restart MySQL service');
try Connection.Disconnect; except end;
Query2.UnPrepare;
Query2.Prepare; // EAssertionFailed
Query2.Open;
Query1.UnPrepare;
Query1.Prepare;
Query1.Open;
finally
TButton(Sender).Enabled := True;
end;
end;
I wish that could help...
Regards.
Re: Access violation after a Mysql disconnection
Posted: Tue 19 Jun 2012 11:54
by AndreyZ
You handle connection losses in the incorrect way. To resume lost connection, you should use the TMyConnection.OnConnectionLost event handler. The OnConnectionLost event handler is used to process fatal errors and perform failover. To make the OnConnectionLost event handler work, you should set the TMyConnection.Options.LocalFailover property to True. Note that to use the OnConnectionLost event handler, you should add the MemData unit to the USES clause of your unit. Here is an example of using the OnConnectionLost event handler:
Code: Select all
procedure TForm1.Button1Click(Sender: TObject);
begin
MyConnection1.Options.LocalFailover := True;
MyConnection1.Open;
end;
procedure TForm1.MyConnection1ConnectionLost(Sender: TObject;
Component: TComponent; ConnLostCause: TConnLostCause;
var RetryMode: TRetryMode);
begin
RetryMode := rmReconnectExecute;
end;
In this case, if connection was lost, MyDAC will try to reconnect and reexecute the abortive operation. For more information, please read the MyDAC documentation.
Re: Access violation after a Mysql disconnection
Posted: Tue 19 Jun 2012 12:41
by zeltron73
Thank you for your response, but if you test the piece of code I posted before, you should notice the error with or without the "LocalFailOver" property set (and in my case, the event is NEVER triggered)...
In fact, the error is due to the prepared requests; if I unprepare Query1 or Query2 before connection is lost, the error disappears. If both requests are prepared before connection is lost, you catch the EAssertionFailed exception which causes AV.
Thanks for your help.
Regards.
Re: Access violation after a Mysql disconnection
Posted: Wed 20 Jun 2012 06:26
by AndreyZ
I cannot reproduce the problem. Your code works without any problems. Please try creating a small sample to demonstrate the problem and send it to andreyz*devart*com . Also please specify the following:
- the exact version of MyDAC. You can learn it from the About sheet of TMyConnection Editor;
- the exact version of your IDE;
- the exact version of MySQL server and client. You can learn it from the Info sheet of TMyConnection Editor.
Re: Access violation after a Mysql disconnection
Posted: Fri 22 Jun 2012 15:31
by zeltron73
Hello,
Can anybody reproduce the error with MyDAC 7.1.6 ?
Code: Select all
procedure TForm1.Button1Click(Sender: TObject);
const
QUERY_1 = 'SELECT t1.* FROM (' +
'(SELECT 1 AS Field1, "string1" AS Field2) UNION ' +
'(SELECT 2, "string2")' +
') t1 WHERE t1.Field1 = :Key';
QUERY_2 = 'SELECT t2.* FROM (' +
'(SELECT 3 AS Field1, "string3" AS Field2) UNION ' +
'(SELECT 4, "string4")' +
') t2 WHERE t2.Field1 = :Key';
var
sei: TShellExecuteInfo;
Connection: TMyConnection;
Query1: TMyQuery;
Query2: TMyQuery;
Test: string;
begin
TButton(Sender).Enabled := False;
Connection := TMyConnection.Create(Self);
Connection.Server := 'localhost';
Connection.Username := 'root';
Connection.Password := 'mysql';
Connection.LoginPrompt := False;
Query1 := TMyQuery.Create(Self);
Query1.Connection := Connection;
Query2 := TMyQuery.Create(Self);
Query2.Connection := Connection;
try
Query1.SQL.Text := QUERY_1;
Query1.ParamByName('Key').AsInteger := 1;
Query1.Prepare;
Query1.Open;
Query2.SQL.Text := QUERY_2;
Query2.ParamByName('Key').AsInteger := 3;
Query2.Prepare;
Query2.Open;
Test := Query1.FieldByName('Field2').AsString;
Test := Query2.FieldByName('Field2').AsString;
// to simulate a Mysql connection failure
// stops the Mysql service and wait by issuing "NET STOP/START MYSQL"
FillChar(sei, SizeOf(ShellExecuteInfo), 0);
sei.cbSize := SizeOf(ShellExecuteInfo);
sei.Wnd := Handle;
sei.fMask := SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb := 'open';
sei.lpFile := 'NET.EXE';
sei.lpParameters := 'STOP MYSQL'; // NET = synchronous command (unlike SC)
sei.lpDirectory := PChar(GetCurrentDir);
if ShellExecuteEx(@sei) then
begin
while (MsgWaitForMultipleObjects(1, sei.hProcess, False, 500, QS_ALLINPUT) <> WAIT_OBJECT_0) do
Application.ProcessMessages;
CloseHandle(sei.hProcess);
// starts the Mysql service and wait
sei.lpParameters := 'START MYSQL';
if ShellExecuteEx(@sei) then
begin
while (MsgWaitForMultipleObjects(1, sei.hProcess, False, 500, QS_ALLINPUT) <> WAIT_OBJECT_0) do
Application.ProcessMessages;
CloseHandle(sei.hProcess);
end;
end
else
raise Exception.Create('Unable to restart MySQL service');
try
Query1.UnPrepare;
Query2.UnPrepare; // raises an EMySqlException (lost connection)
except
on E: Exception do
begin
MessageDlg(E.Message, mtError, [mbOK], 0);
Connection.Disconnect;
end;
end;
Query1.Prepare;
Query1.Open;
Query2.Prepare; // EAssertionFailed => EAccessViolation
Query2.Open;
finally
TButton(Sender).Enabled := True;
Connection.Free;
Query1.Free;
Query2.Free;
end;
end;
Thanks for your help...
Re: Access violation after a Mysql disconnection
Posted: Mon 25 Jun 2012 15:01
by zeltron73
The bug still persists with the version 7.2.7 for Delphi 5 on Windows XP (x86) and Windows 7 (x64) and MySQL 5.5.25 (x86 or x64)...
In fact, the error appears when "Query2.UnPrepare" is called, and when the EMysqlException is unwound... the cleanup code is perhaps not properly called. If you prevent the exception from being raised, the AV disappears !!!
Any suggestions ?
Re: Access violation after a Mysql disconnection
Posted: Mon 02 Jul 2012 12:47
by AndreyZ
Thank you for the information. We have reproduced the problem and investigation of the problem is in progress. As soon as we have any results we will let you know.
Re: Access violation after a Mysql disconnection
Posted: Fri 20 Jul 2012 09:22
by AndreyZ
We have fixed this problem. This fix will be included in the next MyDAC build.
Re: Access violation after a Mysql disconnection
Posted: Mon 23 Jul 2012 13:25
by zeltron73
Thanks a lot for your responsiveness.
Re: Access violation after a Mysql disconnection
Posted: Tue 24 Jul 2012 06:38
by AndreyZ
Feel free to contact us if you have any further questions about MyDAC.