Delphi 2010 : UniDAC vs Indy-MultiThread safety handleing method - mysql

I am doing develop Indy based application.
Server has several Indy TCP Server components.
So It works under multi-threads and handles mysql db.
I have faced one problem.
That is about the exceptions of MySQL DB in threads.
When serveral threads attack to same db table, then It says me like follows
UniQuery_Mgr: Duplicate field name 'id'
UniQuery_Mgr: Field 'grp_id' not found //of course grp_id field is really existed.
Assertion failure (C:\Program Files (x86)\unidac539src\Source\CRVio.pas, line 255)
Commands out of sync; You can't run this command now
ReceiveHeader: Net packets out of order: received[0], expected[1]
UniQuery_Mgr: Cannot perform this operation on a closed dataset
How to do I ? UniQuery_Mgr is TUniQuery component.
and my query handling code is normally like this
Code 1
sql := 'SELECT * FROM data_writed;';//for example
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
Code 2
try
sql := 'SELECT * FROM gamegrp_mgr;';
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
if UniQuery_Mgr.RecordCount > 0 then
begin
MAX_GAME_GROUP_COUNT := UniQuery_Mgr.RecordCount + 1;
UniQuery_Mgr.First;
i := 1;
while not UniQuery_Mgr.Eof do
begin
Game_Group_ID[i] := UniQuery_Mgr.FieldByName('grp_id').AsInteger;
Game_Game_ID[i] := UniQuery_Mgr.FieldByName('game_id').AsInteger;
UniQuery_Mgr.Next;
Inc(i);
end;
end;
except
on E : Exception do
begin
EGAMEMSG := Format('GAME group read error: <%s> # %s',[ E.ToString, DateTimeToStr(now)]);
Exit;
end;
end;
Code 3
try
sql := 'UPDATE data_writed SET write_gamegrp = ' + QuotedStr('0') + ';';
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
except
on E : Exception do
begin
EGAMEMSG := Format('data updating error: <%s> # %s',[ E.ToString, DateTimeToStr(now)]);
Exit;
end;
end;
My handling DB components is bad ? Other thread-safe method is existed???

Related

Initialize TSQLConnection Driver non-GUI App

I'm trying to create TSQLConnection at run-time on non-GUI application.
Uses Data.DB, Data.SqlExpr, Data.DBXMSSQL;
...
procedure TFoo.InitializeDB;
begin
if NOT Assigned(SQLCon) then exit;
SQLCon.Params.Clear;
SQLCon.ConnectionName := 'ReportsCon';
SQLCon.DriverName := 'MSSQL';
SQLCon.LoadParamsFromIniFile('C:\ConfigTest\DBTest.ini');
try
SQLCon.Connected := True;
except
on E: Exception Do
LastErr := E.Message;
end;
end;
I got this error
DBX Error: Driver could not be properly initialized. Client library may be missing, not installed properly, of the wrong version, or the driver may be missing from the system path.
For test purpose i created a new GUI project i dropped TButton on the form with the below OnClick event
Uses Data.DB, Data.SqlExpr, Data.DBXMSSQL;
...
procedure TForm12.btn1Click(Sender: TObject);
var
SQLConnection: TSQLConnection;
begin
SQLConnection := TSQLConnection.Create(nil);
try
SQLConnection.ConnectionName := 'ReportsCon';
SQLConnection.DriverName := 'MSSQL';
SQLConnection.LoadParamsFromIniFile('C:\ConfigTest\DBTest.ini');
try
SQLConnection.Connected := True;
except
on E: Exception Do
ShowMessage(E.Message);
end;
finally
SQLConnection.Free;
end;
end;
The code run as expected with no error.
Both EXE and dbxmss.dll are on the same folder
C:\Users\$Name\Documents\Embarcadero\Studio\Projects\Win32\Debug
What am I missing here ?

Getting the "Arguments are of the wrong type..." exception just by assigning query text

I have downloaded and installed MySQL Connector 5.1 x64 so I can use MySQL with Delphi. I can make connection with ODBC and do a connection from my Delphi environment and from MySQL Workbench.
But, when I build my Query at runtime, I get an error saying:
Project AAA.exe raised exception class EOleException with message 'Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another'. Process stopped. Use Step or Run to continue.
My code:
qDates := TADOQuery.Create(Component);
qDates.Connection := FConnection;
qDates.SQL.Text :=
'select ' +
' * ' +
'from ' +
' resulttable ' +
'where ' +
' oid = :oid ' +
' and datedial >= :datebegin and datedial <= :dateend'; // <<-- Exception here
Details:
The exception happens right on the text assignment, before I have a chance to configure parameters.
If I comment out the where clause the assignment goes fine.
This is similar to Using parameters with ADO Query (mysql/MyConnector) but the difference is that I assign whole text at once and I get the exception before I have a chance to configure parameters.
The puzzling part - exact same code works fine on my other machine, but I can not figure out what is different.
Hence the question - what could cause the above exception outside of the Delphi code and MySQL server?
This seems to be a quirk with the MySQL ODBC provider.
If you assign the connection after setting the SQL text, then it will work.
The reason why can be found here.
qDates := TADOQuery.Create(Component);
// do net yet assign TADOConnection to prevent roundtrip to ODBC provider
qDates.SQL.Text :=
'select ' +
' * ' +
'from ' +
' resulttable ' +
'where ' +
' oid = :oid ' +
' and datedial >= :datebegin and datedial <= :dateend';
qDates.Connection := FConnection;
UPDATE
This QC entry explains the exact reason for this problem.
In short, the ADODB unit, patch this line from the RefreshFromOleDB procedure :
Parameter.Attributes := dwFlags and $FFFFFFF0; { Mask out Input/Output flags }
To:
if dwFlags and $FFFFFFF0 <= adParamSigned + adParamNullable + adParamLong then
Parameter.Attributes := dwFlags and $FFFFFFF0; { Mask out Input/Output flags }

Application closing when Activating the Connection on FireDAC

I developed an application that uses FireDAC to connect to a MySQL Database.
But when I try to open it on a workstation, at the moment that I set the Connected := True; on the TFDConnection, the application closes itself without showing an exception. It is surrounded with a try...except, but still doesn't show no error message at all. Here's the code I'm using to set the connection:
procedure TfrmServidor.confConnection;
begin
with conMySQL do begin
DriverName := LeXML.Strings[5];
Params.Add('Server=' + LeXML.Strings[3]);
Params.Add('Port=' + LeXML.Strings[4]);
Params.Add('Database=' + LeXML.Strings[0]);
Params.Add('User_Name=' + LeXML.Strings[1]);
Params.Add('Password=' + LeXML.Strings[2]);
ShowMessage(Params.Text);
end;
try
conMySQL.Connected := True;
except
on e : Exception do
ShowMessage(e.Message);
end;
end;
Where LeXML is a function that reads a XML file with the properties and returns the values on a TStringList.
What is it that i'm doing wrong?
The ShowMessage with the Params Text returns the following:
[Window Title]
Servidor
[Content]
DriverID=MySQL
Server=10.1.1.16
Port=3306
Database=treinamentos
User_Name=treinamentos
Password=masterkey
[OK]
Can anyone help?

Inno Setup GetExceptionMessage returns empty message

in Inno Setup script GetExceptionMessage returns empty message (it contains only colon ":" sign). The last version of Inno Setup (5.4.2) is used.
try
Log('Create IISNamespace');
// Create IIS namespace object
if Length(virtualDirectoryName) > 0 then
begin
IIS := CreateOleObject('IISNamespace');
Log('Get IIsWebService');
WebSite := IIS.GetObject('IIsWebService', IISServerName + '/w3svc');
Log('Get IIsWebServer');
WebServer := WebSite.GetObject('IIsWebServer', IISServerNumber);
Log('Get IIsWebVirtualDir');
WebRoot := WebServer.GetObject('IIsWebVirtualDir', 'Root');
Log('Delete IIsWebVirtualDir');
WebRoot.Delete('IIsWebVirtualDir', virtualDirectoryName);
WebRoot.SetInfo();
end;
except
MsgBox(ExpandConstant('{cm:IISException,'+ GetExceptionMessage +'}'),
mbInformation, mb_Ok);
Log('Uninstall IIS 6 exception: ' + GetExceptionMessage);
end;
The exception occurs during deleting IIsWebVirtualDir.
Is there any way to get exception type or real exception message?
Thanks, Denis.
I just wrote the following example to see if either GetExceptionMessage or ShowExceptionMessage are broken. I used both Inno setup 5.4.2 Unicode and Ansi versions.
[Setup]
AppName=Test
AppVersion=1.5
DefaultDirName={pf}\test
[Code]
function InitializeSetup(): Boolean;
var
I: Integer;
begin
try
I := I div 0; // Raise an exception
except
MsgBox(GetExceptionMessage,
mbError, MB_OK);
ShowExceptionMessage;
end;
result := false;
end;
I also ran CodeAutomation.iss that ships and it worked as expected. Which is contrary to the comment made by Alex K. that it may be broken.
Now that I know that the routines should work, I took your code and made the following setup test Script and ran it and it raised an exception on not finding the ISSNamespace as I don't have it installed.
[Setup]
AppName=Test
AppVersion=1.5
DefaultDirName={pf}\test
[CustomMessages]
IISException =ISS Exception " %1 " occured.
[Code]
const
IISServerName = 'localhost';
IISServerNumber = '1';
IISURL = 'http://127.0.0.1';
function InitializeSetup(): Boolean;
var
IIS, WebSite, WebServer, WebRoot, VDir: Variant;
virtualDirectoryName : String;
begin
virtualDirectoryName := 'test';
try
Log('Create IISNamespace');
// Create IIS namespace object
if Length(virtualDirectoryName) > 0 then
begin
IIS := CreateOleObject('IISNamespace');
Log('Get IIsWebService');
WebSite := IIS.GetObject('IIsWebService', IISServerName + '/w3svc');
Log('Get IIsWebServer');
WebServer := WebSite.GetObject('IIsWebServer', IISServerNumber);
Log('Get IIsWebVirtualDir');
WebRoot := WebServer.GetObject('IIsWebVirtualDir', 'Root');
Log('Delete IIsWebVirtualDir');
WebRoot.Delete('IIsWebVirtualDir', virtualDirectoryName);
WebRoot.SetInfo();
end;
except
MsgBox(ExpandConstant('{cm:IISException,'+ GetExceptionMessage +'}'),
mbInformation, mb_Ok);
Log('Uninstall IIS 6 exception: ' + GetExceptionMessage);
end;
end;
But I made a fatal flaw during the construction of this script that could be your problem.
Check your [CustomMesssages] section make sure you have %1 in the message. Otherwise nothing is returned.

mysql call to libmysql.dll to get my app to automatically reconnect after mysql timeout

I am using autohotkey to make mysql calls. The mysql interface was deciphered by referencing a visual basic api to mysql.
I am using the mysql connect calls referenced in this post: http://www.autohotkey.com/forum/viewtopic.php?t=12482
I would like to add a dllcall to replicate this perl call to mysql_options...
mysql_options(mysql, MYSQL_OPT_RECONNECT, &true);
It is my understanding that this call would enable my program to gracefully reconnect to mysql after the standard 8 hour mysql timeout. I want my application to remain up indefinitely.
Here is my code. A reference on googles source code libary suggests that the reconnect constant is 20. Everything works except the mysql_opt_reconnect call.
Can anyone help me determine the proper call to libmysql.dll to get my app to automatically reconnect after mysql timeout has occurred?
;============================================================
; mysql.ahk
;
; Provides a set of functions to connect and query a mysql database
;============================================================
FileInstall, libmysql.dll, %A_AppData%\libmysql.dll, 1
;============================================================
; Connect to mysql database and return db handle
;
; host = DTWRO-WS0061
; user = alan
; password = *******
; database = rush
;============================================================
dbConnect(host,user,password,database){
if (A_IsCompiled) {
ExternDir := A_AppData
} else {
ExternDir := A_WorkingDir
}
hModule := DllCall("LoadLibrary", "Str", ExternDir "\libmySQL.dll")
If (hModule = 0)
{
MsgBox 16, MySQL Error 233, Can't load libmySQL.dll from directory %ExternDir%
ExitApp
}
db := DllCall("libmySQL.dll\mysql_init", "UInt", 0)
If (db = 0)
{
MsgBox 16, MySQL Error 445, Not enough memory to connect to MySQL
ExitApp
}
; figure out how to turn on reconnect call!
; mysql_options(mysql, MYSQL_OPT_RECONNECT, &true);
value := DllCall("libmySQL.dll\mysql_options"
, "UInt", db
, "UInt", 20 ; is this the correct constant which represents MYSQL_OPT_RECONNECT?... see below
, "UInt", 1) ; true
connection := DllCall("libmySQL.dll\mysql_real_connect"
, "UInt", db
, "Str", host ; host name
, "Str", user ; user name
, "Str", password ; password
, "Str", database ; database name
, "UInt", 3306 ; port
, "UInt", 0 ; unix_socket
, "UInt", 0) ; client_flag
If (connection = 0)
{
HandleMySQLError(db, "Cannot connect to database")
Return
}
serverVersion := DllCall("libmySQL.dll\mysql_get_server_info", "UInt", db, "Str")
;MsgBox % "Ping database: " . DllCall("libmySQL.dll\mysql_ping", "UInt", db) . "`nServer version: " . serverVersion
return db
}
;============================================================
; mysql error handling
;============================================================
HandleMySQLError(db, message, query="") { ; the equal sign means optional
errorCode := DllCall("libmySQL.dll\mysql_errno", "UInt", db)
errorStr := DllCall("libmySQL.dll\mysql_error", "UInt", db, "Str")
MsgBox 16, MySQL Error: %message%, Error %errorCode%: %errorStr%`n`n%query%
Return
}
;============================================================
; mysql get address
;============================================================
GetUIntAtAddress(_addr, _offset)
{
local addr
addr := _addr + _offset * 4
Return *addr + (*(addr + 1) << 8) + (*(addr + 2) << 16) + (*(addr + 3) << 24)
}
;============================================================
; process query
;============================================================
dbQuery(_db, _query)
{
local resultString, result, requestResult, fieldCount
local row, lengths, length, fieldPointer, field
query4error := RegExReplace(_query , "\t", " ") ; convert tabs to spaces so error message formatting is legible
result := DllCall("libmySQL.dll\mysql_query", "UInt", _db , "Str", _query)
If (result != 0) {
errorMsg = %_query%
HandleMySQLError(_db, "dbQuery Fail", query4error)
Return
}
requestResult := DllCall("libmySQL.dll\mysql_store_result", "UInt", _db)
if (requestResult = 0) { ; call must have been an insert or delete ... a select would return results to pass back
return
}
fieldCount := DllCall("libmySQL.dll\mysql_num_fields", "UInt", requestResult)
Loop
{
row := DllCall("libmySQL.dll\mysql_fetch_row", "UInt", requestResult)
If (row = 0 || row == "")
Break
; Get a pointer on a table of lengths (unsigned long)
lengths := DllCall("libmySQL.dll\mysql_fetch_lengths" , "UInt", requestResult)
Loop %fieldCount%
{
length := GetUIntAtAddress(lengths, A_Index - 1)
fieldPointer := GetUIntAtAddress(row, A_Index - 1)
VarSetCapacity(field, length)
DllCall("lstrcpy", "Str", field, "UInt", fieldPointer)
resultString := resultString . field
If (A_Index < fieldCount)
resultString := resultString . "|" ; seperator for fields
}
resultString := resultString . "`n" ; seperator for records
}
; remove last newline from resultString
resultString := RegExReplace(resultString , "`n$", "")
Return resultString
}
It took me while to think outside the box, but I finally found a solution that works very well.
I simply added a settimer command to re-connect to the mysql database after 8 hours. 8 hours is the default database connection timeout.
Now the AHK app can remain running indefinitely and is always connected to the database!
Even better... I used an oop class to retain the mysql connection parameters, so that when the mysql connection timed out and a new mysql call is made, it can automatically reconnect.