Page 1 of 1

AV when local order is case insensitive

Posted: Mon 04 Jun 2012 08:15
by zeltron73
Hello,

I use MyDAC 7.1.6 with source code under Windows 7 and Delphi 5.

When I set .IndexFieldNames using case insensitive order, I get an Assertion exception (MemData.pas line 2666) or an Access Violation in dac50.bpl when I retrieve data. If I use case sensitive, the exception disappears... It is difficult to reproduce the problem, because the exception is the result of heap corruption (the content of a TFieldDesc object is corrupted) and appears randomly.
I noticed that you must set Connection.Options.DefaultSortType := stCaseInsensitive after connection has been established, if not, SortColumn.SortType will always be stCaseSensitive...

Has anybody the same problem ?

Thanks for your help.

Re: AV when local order is case insensitive

Posted: Tue 05 Jun 2012 15:08
by AndreyZ
Hello,

I cannot reproduce the problem. Please try creating a small sample to demonstrate the problem and send it to me, including a script to create and fill a table. Also please specify the exact version of MySQL server and client. You can learn it from the Info sheet of TMyConnection Editor.

Re: AV when local order is case insensitive

Posted: Wed 06 Jun 2012 07:31
by zeltron73
Hello Andrey,

I have found the cause of the error which is very odd indeed and drives me crazy since a couple of days... And here is the code to reproduce it.
Create a new project and add a TMyConnection and a TMyQuery component on the form; add a button and copy the code below in the OnClick event handler :

Code: Select all

procedure TForm1.Button1Click(Sender: TObject);
begin
  Connection.Options.Charset := 'latin1';
  Connection.Connect;
  // if you comment the next line, the AV disappears
  Connection.Options.DefaultSortType := stCaseInsensitive;

  Query.SQL.Text := 'SELECT * FROM (' +
    '(SELECT 1 AS Field1, "BBB" AS Field2) UNION ' +
    '(SELECT 2, "AAA")) t ' +
    'WHERE &Condition';
  Query.MacroByName('Condition').Value := 'TRUE';
  Query.IndexFieldNames := 'Field2';
  Query.Open;
  Query.LocateEx('Field2', 'AAA', [lxCaseInsensitive]);
  Query.RecNo := 2;

  Query.MacroByName('Condition').Value := 'Field1 > 0';
  Query.IndexFieldNames := '';
  Query.Open;
  Query.RecNo := 2; // raise an Access Violation exception
end;
Explanation: when the second "Query.MacroByName('Condition').Value" is called, the dataset will be closed, which calls TMemData.FreeData. But TMemData.FRecordNoCache continues to reference items that have just been released.

To correct the bug, I suggest to add the following line in the TMemData.InitData method just after BlockMan.FirstFree:
SetLength(FRecordNoCache, 0); // to clear the cache


I discover another dysfunction, the .Options.DefaultSortType is only taken into account only if the connection is opened (idem for DisconnectedMode, EnableBCD, EnableFmtBCD). Why these options are not set when the connection is opened ?

Regards.

Re: AV when local order is case insensitive

Posted: Wed 06 Jun 2012 08:55
by zeltron73
If the exception didn't appear, add this unit at the first place in the "uses" of your .dpr file

Code: Select all

unit MemMgr;

interface

implementation

uses
  Windows;
 
{ Memory Management }

const
  HEAP_MAX_SIZE = $100000;
  HEAP_GENERATE_EXCEPTIONS = $00000004;
  HEAP_NO_SERIALIZE = $00000001;
  HEAP_ZERO_MEMORY = $00000008;

var
  PrivateHeap: THandle;
  SavedMemoryManager: TMemoryManager;
 
function InternalGetMem(Size: Integer): Pointer;
begin
  Result := HeapAlloc(PrivateHeap, HEAP_GENERATE_EXCEPTIONS or HEAP_NO_SERIALIZE or HEAP_ZERO_MEMORY, Size);
end;
 
function InternalFreeMem(P: Pointer): Integer;
begin
  Result := Integer(not HeapFree(PrivateHeap, HEAP_NO_SERIALIZE, P))
end;
 
function InternalReallocMem(P: Pointer; Size: Integer): Pointer;
begin
  Result := HeapReAlloc(PrivateHeap, HEAP_GENERATE_EXCEPTIONS or HEAP_NO_SERIALIZE or HEAP_ZERO_MEMORY, P, Size)
end;
 
const
  MemoryManager: TMemoryManager = (
    GetMem: InternalGetMem;
    FreeMem: InternalFreeMem;
    ReallocMem: InternalReallocMem
  );
 
initialization
  PrivateHeap := HeapCreate(HEAP_GENERATE_EXCEPTIONS or HEAP_NO_SERIALIZE, 0, HEAP_MAX_SIZE);
  GetMemoryManager(SavedMemoryManager);
  SetMemoryManager(MemoryManager);

finalization
  HeapDestroy(PrivateHeap);
  SetMemoryManager(SavedMemoryManager);
end.
and the following code will raise the AV...

Code: Select all

procedure TForm1.Button1Click(Sender: TObject);
begin
  Connection.Options.Charset := 'latin1';
  Connection.Connect;
  Connection.Options.DefaultSortType := stCaseInsensitive;
  
  Query.SQL.Text := 'SELECT * FROM (' +
    '(SELECT 1 AS Field1, "BBB" AS Field2) UNION ' +
    '(SELECT 2, "AAA")) t ' +
    'WHERE &Condition';
  Query.MacroByName('Condition').Value := 'TRUE';
  Query.IndexFieldNames := 'Field2';
  Query.Open;
  Query.LocateEx('Field2', 'AAA', [lxCaseInsensitive]);
  Query.RecNo := 2;

  Query.MacroByName('Condition').Value := 'Field1 > 0';
  StringOfChar(' ', 4096);  // to use the heap
  Query.IndexFieldNames := '';
  StringOfChar(' ', 4096);
  Query.Open;
  StringOfChar(' ', 4096);
  Query.RecNo := 2;
end;
Regards.

Re: AV when local order is case insensitive

Posted: Wed 06 Jun 2012 10:26
by AndreyZ
Thank you for the information. We have fixed the problem with setting the DefaultSortType property. This fix will be included in the next MyDAC build.
We have reproduced the problem with AV and the investigation is in progress. We will notify you when we get any results.

Re: AV when local order is case insensitive

Posted: Wed 13 Jun 2012 09:17
by AndreyZ
We have fixed the problem. This fix will be included in the next MyDAC build.