Log On
Embarcadero Home
Watch, Follow, &
Connect with Us
Share This
QualityCentral
Communities
Articles
Blogs
Resources
Downloads
Help
Quality Central
Delphi-BCB
Database
DBExpress
TFMTBcd
TSimpleDataset
TSQLClientDataSet
TSQLConnection
TSQLDataSet
TSQLMonitor
TSQLQuery
TSQLStoredProc
TSQLTable
TSqlTimeStamp
Unicode Support
You are not logged in.
Help
Print
Public Report
Report From:
Delphi-BCB/Database/DBExpress/TSQLConnection
[ Add a report in this area ]
Report #:
58377
Status:
Closed
SQLConnection close leads to invalid SQLConnection.FMetaData value
Project:
Delphi
Build #:
11.0.2902.10471
Version:
11.2
Submitted By:
Peter Sawatzki
Report Type:
Basic functionality failure
Date Reported:
2/17/2008 8:55:35 AM
Severity:
Serious / Highly visible problem
Last Updated:
2/16/2010 11:10:05 PM
Platform:
All platforms
Internal Tracking #:
263319
Resolution:
Cannot Reproduce
(Resolution Comments)
Resolved in Build:
:
Delphi 2009
Duplicate of:
None
Voting and Rating
Overall Rating:
No Ratings Yet
0.00 out of 5
Total Votes:
1
Description
TSQLConnection.DoDisconnect does not assign nil to FMetaData although the corresponding object instance was already freed in a call to TDBXConnection.Close. This leads to access violations in certain situations and potential problems. See steps for a reproducible case that generates an access violation.
Steps to Reproduce:
Notice for the code below to produce an exception, the resulting program has to be executed on a system that has not "seen" dbexpress yet - that means especially no registry entries for dbxdrivers.ini and dbxconnections.ini and also no dbx*.ini file in the application directory itself. If the files are present, the exception is not raised - although memory is corrupted elsewhere. Notice that in my tests the error appears in this combination of SQLServer/MySQL, others may not produce the error exactly.
1. compile the attached console application but modify connection parameters to your username/password/server
2. deploy program to virgin system along with dbxmss30.dll and dbxmys30.dll
3. run program, following exception is thrown on the SQLServerQuery.SQL.Text assignment :
"Exception EAccessViolation in module MetaDataBug.exe at 00004390.
Access violation at address 00404390 in module 'MetaDataBug.exe'. Read of addres
s FFFFFFDD."
Reason: the exception is raised in SqlExpr.Pas, function GetIdOption in line
" if (Assigned(Connection) and (Connection.MetaData is TDBXDatabaseMetaDataEx)) then"
where code tries to access MetaData that is invalid.
Here is the test console app
program MetaDataBug;
{$APPTYPE CONSOLE}
uses
Windows, Messages, SysUtils, Variants, Classes,
WideStrings, FMTBcd, DB, SqlExpr;
var
SQLServerConn, MySQLConn: TSQLConnection;
SQLServerQuery, MySQLQuery: TSQLQuery;
Count: Integer;
begin
// 1st connection SQLServer
SQLServerConn := TSqlConnection.Create(nil);
SQLServerConn.ConnectionName := 'MSSQLConnection';
SQLServerConn.DriverName := 'MSSQL';
SQLServerConn.LibraryName := 'dbxmss30.dll';
SQLServerConn.VendorLib := 'Oledb';
SQLServerConn.GetDriverFunc := 'getSQLDriverMSSQL';
SQLServerConn.Params.Clear;
SQLServerConn.Params.Add( 'HostName=mysqlserver' );
SQLServerConn.Params.Add( 'Database=mysqldb' );
SQLServerConn.Params.Add( 'USER_NAME=myuser' );
SQLServerConn.Params.Add( 'PASSWORD=mypassword' );
SQLServerQuery:= TSQLQuery.Create(nil);
SQLServerQuery.SQLConnection:= SQLServerConn;
// 2nd connection MySQL
MySQLConn := TSqlConnection.Create(nil);
MySQLConn.ConnectionName := 'MySQLConnection';
MySQLConn.DriverName := 'MySQL';
MySQLConn.LibraryName := 'dbxmys30.dll';
MySQLConn.VendorLib := 'LIBMYSQL.dll';
MySQLConn.GetDriverFunc := 'getSQLDriverMYSQL';
MySQLConn.Params.Clear;
MySQLConn.Params.Add( 'HostName=127.0.0.1' );
MySQLConn.Params.Add( 'Database=myMySQLdb' );
MySQLConn.Params.Add( 'USER_NAME=root' );
MySQLConn.Params.Add( 'PASSWORD=mypassword' );
MySQLQuery:= TSQLQuery.Create(nil);
MySQLQuery.SQLConnection:= MySQLConn;
// now
SQLServerConn.Connected:= true;
SQLServerConn.Connected:= false; // this should invalidate SQLServerConn.SQLCOnnection.MetaData but it doesn't
MySQLQuery.SQL.Text:= 'select count(*) as countorders from test.test'; // query any table
MySQLQuery.Active:= true;
Count:= MySQLQuery.FieldByName('countorders').AsInteger;
if count=1000 then Inc(Count); // to suppress compiler warning
// here we get an access violation for the SQLServer/mySQL combination
SQLServerQuery.SQL.Text:= 'select count(*) as countorders from ARTIKEL'; // query any table
WriteLn('Done.');
end.
Workarounds
None
Attachment
None
Comments
Markus Humm at 4/17/2008 4:03:59 AM
-
Can you attach a sample project maybe?
Michal Trybulec at 5/30/2008 2:39:52 AM
-
Well, don't have time to dig into SqlExpr, but the suggested workaround helped!
Using MySQL 5.0.32 with D2007 and Intraweb (9.0.39); all patches applied, Apr08 hotfix too. During tests we would get intermittent AVs when assigning a query to TSQLQuery.SQL.Text. Sometimes worked without problems, sometimes reported an error in one test or another; sometimes worked on one computer, barfed on another, stuff like that.
Unfortunately, I can't give you a project that would reproduce this reliably.
Brian Van Matre at 8/1/2008 8:23:04 AM
-
I am hitting this problem. I'd love to hear the 'suggested workaround'! Thanks.
Brian Van Matre at 8/1/2008 8:30:21 AM
-
More specifically, C++ Builder 2007 Version 11.0.2987.10779.
I have a TSQLQuery component that I use to do simple queries, called
GenericQuery. I use it like this:
vector<MyType*> retVal;
GenericQuery->SQL->Clear();
GenericQuery->SQL->Add("SELECT * FROM table_1 WHERE HIDDEN = 0");
GenericQuery->Open();
while (!GenericQuery->Eof)
{
TField* f1F = GenericQuery->Fields->FieldByName("field_1");
TField* f2F = GenericQuery->Fields->FieldByName("field_2");
TField* f3F = GenericQuery->Fields->FieldByName("field_3");
TField* hidF = GenericQuery->Fields->FieldByName("hidden");
retVal.push_back(new MyType(f1F,f2F,f3F,hidF));
GenericQuery->Next();
}
GenericQuery->Close();
return retVal;
If I call this method the first time, it's fine. If I call it twice in a
row, it's fine. If I call another TSQLQuery on the same connection after
calling this method, and then go back and call this method, I get an access
violation. After the access violation, it works fine again. The violation
occurs at the line 'GenericQuery->SQL->Clear();'. The stack trace looks
like this:
:7c812a5b kernel32.RaiseException + 0x52
:008e3f0a ___raiseDebuggerException + 0x1A
:008ef598 ; ____ExceptionHandler
:008e32ed __ExceptionHandler + 0x1E
:7c90378b ntdll.RtlConvertUlongToLargeInteger + 0x46
:7c90eafa ntdll.KiUserExceptionDispatcher + 0xe
:006FA300 Sqlexpr::TSQLQuery::QueryChanged(Self=????, Sender=????)
:005EF680 Widestrings::TWideStringList::Changed(Self=????)
:004E22D2 TMyTypeOfForm::TMyTypeOfForm(this=:01416E20, Owner=:014C9650)
If I trace way down into the DBX code, the exception ends up being raised on
this line in SqlExpr.pas
if (Assigned(Connection) and (Connection.MetaData is
TDBXDatabaseMetaDataEx)) then
If it matters I'm using an upscene driver and
Firebird 2.0 database.
Again, looking for the workaround to this problem.
Michal Trybulec at 9/24/2008 12:29:05 AM
-
The fix is in the description:
In SqlExpr.pas, in TSQLConnection.DoDisconnect, set FMetaData to NIL
right after FreeAndNil(FDBXConnection);
Code to reproduce:
procedure TYIKDBTests.TestSqlExpr;
const
CCount = 1000;
var
k, LSize: integer;
LConnection: TSQLConnection;
LQuery: TSQLQuery;
LMem: array of pointer;
begin
LConnection := CreateSQLConnection;
try
LSize := LConnection.MetaData.InstanceSize;
LQuery := TSQLQuery.Create(NIL);
try
LQuery.SQLConnection := LConnection;
LQuery.SQL.Text := 'select * from TIMING;';
LQuery.Open;
finally
FreeAndNIL(LQuery);
end;
SetLength(LMem, CCount);
for k := 0 to CCount - 1 do
begin
GetMem(LMem[k], LSize);
FillChar(LMem[k]^, LSize, $AA);
end;
for k := 0 to CCount - 1 do
FreeMem(LMem[k]);
LQuery := TSQLQuery.Create(NIL);
try
LQuery.SQLConnection := LConnection;
LQuery.SQL.Text := 'select * from TIMING;';
LQuery.Open;
finally
FreeAndNIL(LQuery);
end;
finally
FreeAndNIL(LConnection);
end;
end;
The GetMems in the middle are needed to overwrite memory that FMetaData is pointing to (and was freed when FDBXConnection was freed).
CreateSQLConnection is a function of the form:
function CreateSQLConnection: TSQLConnection;
begin
Result := TSQLConnection.Create(NIL);
Result.DriverName := 'MySQL';
Result.GetDriverFunc := 'getSQLDriverMYSQL';
Result.LibraryName := 'dbxmys30.dll';
Result.VendorLib := 'LIBMYSQL.dll';
Result.Params.Values['HostName'] := CYIKDBServerIP;
Result.Params.Values['Database'] := CYIKDBDatabase;
Result.Params.Values['User_Name'] := CYIKDBUserName;
Result.Params.Values['Password'] := CYIKDBPassword;
Result.KeepConnection := FALSE;
Result.Connected := TRUE;
end;
Tomohiro Takahashi at 2/17/2010 12:27:20 AM
-
Hi,
Today, we have encountered same issue with Delphi 2007...
I think a possible workaround is to set Connected property of TSQLConnection to True before manipulate TSQLQuery.SQL property.
> In SqlExpr.pas, in TSQLConnection.DoDisconnect, set FMetaData to NIL
right after FreeAndNil(FDBXConnection
AFAIK, this issue is already fixed in Delphi 2009.
View Your Reports
Search
Server Response from: ETNACODE01
Developer Tools
Blackfish SQL
C++Builder
Delphi
Delphi Prism
InterBase
JBuilder
J Optimizer
RadPHP
3rdRail & TurboRuby
Database Tools
Change Manager
DBArtisan
DB Optimizer
ER/Studio
Performance Center
Rapid SQL
Technical Articles
Tutorials
White Papers
Press Releases
Newsletters
Add Content (GetPublished)
Audio
Audio & Video
Video
Bugs & Suggestions (QualityCentral)
Discussion Forums
Examples (CodeCentral)
Tags
Technology Partners
Downloads
Free Trials
Registered User Downloads
Beta Programs
Add Content (GetPublished)
Articles
Blogs
Bugs & Suggestions (QualityCentral)
Chats
Discussion Forums
Examples (CodeCentral)
Member Services
About
Connect with Us