Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Database/dbExpressCore    [ Add a report in this area ]  
Report #:  57849   Status: Open
No code to free or close ParameterRowHandle in TDBXDynalinkCommand.CreateParameterRow cause memory leak
Project:  Delphi Build #:  11.0.2902.10471
Version:    11.1 Submitted By:   Chee Yang Chau
Report Type:  Minor failure / Design problem Date Reported:  2/4/2008 5:28:45 PM
Severity:    Commonly encountered problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All platforms Internal Tracking #:   258300
Resolution: None (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
Description
A method in unit DBXDynalink.pas may cause serious memory leak in massive database operation using DBX4 drivers:

function TDBXDynalinkCommand.CreateParameterRow: TDBXRow;
var
  ParameterRowHandle: TDBXRowHandle;
begin
  if FCommandHandle = nil then
    Open;

CheckResult(FMethodTable.FDBXCommand_CreateParameterRow(FCommandHandle, ParameterRowHandle));

  Result := TDBXDynalinkRow.Create(FDBXContext, FMethodTable, ParameterRowHandle);
end;

The ParameterRowHandle is pass to TDBXDynalinkRow instance but TDBXDynalinkRow class didn't free the ParameterRowHandle.

I have used AQTime to do an allocation profile and found the leakage happens there.

This problem can't use FastMM or ReportMemoryLeaksOnShutdown to show the leakage as it occurs in the DBX4 dll files.  If possible, you should compile the DBX4 dll files with "Include TD32 debug info" and include the DBX4 dll file to AQTime module.
Steps to Reproduce:
1. Open New VCL Application
2. Create TSQLConnection instance
3. Create TSQLDataSet instance
4. setup the connection to a database
5. Use the TSQLDataSet to query a table for 10000 times

e.g.:

for i := 1 to 10000 do begin
  SQLDataSet1.Open;
  SQLDataSet1.Close;
end;

6. Build both test application and DBX4 library for AQTime profiling. (e.g. include TD32 debug info, Stack Frames On)

7. Use Allocation Profiler in AQTime to run the application.

8. You will see lot of memory leaks.
Workarounds
Extract files in QC57849.ZIP and include in your project files will solve the leakage
Attachment
QC57849.zip
Comments

Boris Damianov at 10/20/2008 2:20:45 AM -
Hello

I have tryed the workaround but the following error occur:

---------------------------
Debugger Exception Notification
---------------------------
Project DBXTest.exe raised exception class EAccessViolation with message 'Access violation at address 00CCDA6C in module 'dbxmss30.dll'. Read of address 00D101C0'.
---------------------------
Break   Continue   Help  
---------------------------

Any suggestions please.

Chee Yang Chau at 1/18/2009 11:34:30 PM -
I encounter the memory leak in my own DBX 4 dynalink driver written for Firebird database.  For MS SQL server, you don't need this patch.

I am not sure how dbxmss30.dll manage the parameter row.  For my case, I do need the FDBXBase_Close to be invoke some where in order to free the object I created for parameter row.

Chee Yang Chau at 9/9/2009 1:08:47 AM -
I have found a workaround solution without relying on FDBXBase_Close to free parameter_row instance.

When DBXCommand_CreateParameterRow is invoke, I keep a reference of the Parameters to a list.  When DBXCommand instance is close, the Parameters instance will be released:

function DBXCommand_CreateParameterRow(Handle: TDBXCommandHandle; out
    Parameters: TDBXRowHandle): TDBXErrorCode; stdcall;
begin
  Parameters := nil;
  Result := IDBXCommand(Handle).CreateParameterRow(Parameters);
end;

function TDBXCommand_Firebird.Close: TDBXErrorCode;
var i: integer;
    o: TDBXRowHandle;
begin
  if Assigned(FDSQL) then begin
    FDSQL.Close(StatusVector);
    if not StatusVector.CheckResult(Result, TDBXErrorCodes.VendorError) then Exit;
  end;
  for i := 0 to GetParameterRows.Count - 1 do begin
    o := GetParameterRows[i];
    IDBXBase(o).Close;
    IDBXBase(o) := nil;
  end;

  Result := TDBXErrorCodes.None;
end;

function TDBXCommand_Firebird.CreateParameterRow(out aRow: TDBXRowHandle):
    TDBXErrorCode;
var M: IMetaDataProvider;
    o: IDBXBase;
begin
  M := TMetaDataProvider_Firebird.Create(FDSQL.o_SQLDA);
  o := TDBXRow_Firebird.Create(FConnection, FDBHandle, M, FDSQL, (FConnection as IDBXConnection_Firebird).TrimChar);

  IDBXBase(aRow) := o;
  GetParameterRows.Add(aRow);

  Result := TDBXErrorCodes.None;
end;

Server Response from: ETNACODE01