Page 1 of 1

TWideStringField+too long strings+TSmart...-> Stack overflow

Posted: Mon 06 Feb 2012 09:43
by cis-wurzen
When assigning too long strings to a TWideStringField of a TSmartQuery in Unicode Delphi versions you may get a stack overflow when posting the record. (Tested XE and XE2 [Win32] with ODAC 8.1.4) This could be partly a Embarcadero Data bug with missing(?) TWideStringField truncation, is subject to be reported there, but however I am not able to generate such complete failures with TClientDataSet. Please review your code anyway.

The test case for this is the following:

SQL for the test table

Code: Select all

CREATE TABLE ODACWIDESTRINGTRUNCATION (
  ID     NUMBER(9,0),
  FIELD1 VARCHAR2(100)
);

ALTER TABLE ODACWIDESTRINGTRUNCATION ADD CONSTRAINT ODACWIDESTRINGTRUNCATIONPK PRIMARY KEY (ID);
Delphi console application

Code: Select all

program OdacWideStringFieldTruncation;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Ora,
  OraSmart;

{
CREATE TABLE ODACWIDESTRINGTRUNCATION (
  ID     NUMBER(9,0),
  FIELD1 VARCHAR2(100)
);

ALTER TABLE ODACWIDESTRINGTRUNCATION ADD CONSTRAINT ODACWIDESTRINGTRUNCATIONPK PRIMARY KEY (ID);
}

const
  cServer = 'YourServer';
  cUsername = 'YourUser';
  cPassword = 'YourPassword';

function TestWideStringTruncation: Boolean;
var
  se: TOraSession;
  qr: TSmartQuery;
  L: Integer;
begin
  se := TOraSession.Create(nil);
  qr := TSmartQuery.Create(nil);
  try
    se.Server := cServer;
    se.Username := cUserName;
    se.Password := cPassword;
    se.Options.UseUnicode := True;
    with qr do
    begin
      Session := se;
      SQL.Add('DELETE FROM ODACWIDESTRINGTRUNCATION');
      ExecSQL;
      SQL.Clear;
      SQL.Add('SELECT * FROM ODACWIDESTRINGTRUNCATION');
      Keyfields := 'ID';
      Open;

      Insert;
      FieldByName('ID').AsInteger := 1;
      FieldByName('FIELD1').AsString := StringOfChar('A', 5000);
      Post;

      L := Length(FieldByName('FIELD1').AsString);
      Result := L = 100;
      if not Result then
        WriteLn('FAIL #1 - ', L);

      Close;
    end;
  finally
    se.Free;
  end;
end;

begin
  try
    if TestWideStringTruncation then
      WriteLn('PASS')
    else
      WriteLn('FAIL');
  except
    on E: Exception do
    begin
      WriteLn('FAIL - Exception Error');
      WriteLn('  E.ClassName = ', E.ClassName);
      WriteLn('    E.Message = ', E.Message);
    end;
  end;
  ReadLn;
end.
Steps:
- execute the script
- create a new console application in Delphi and paste the code
- adjust the constants cServer, cUserName and cPassword
- compile and run the example

expected: output is PASS
actual:

---------------------------
Debugger Exception Notification
---------------------------
Project OdacWideStringFieldTruncation.exe raised exception class $C00000FD with message 'stack overflow at 0x7732f9cd'.
---------------------------
Break Continue Help
---------------------------

With lower string lengths (<= 4097; FWIW yes, lower or equal 97 and not 96) you get the following output
FAIL #1 - 200
FAIL

For this error I've changed this

Code: Select all

      FieldByName('FIELD1').AsString := StringOfChar('A', 5000);
to that

Code: Select all

      FieldByName('FIELD1').AsString := StringOfChar('A', 200);

Posted: Mon 06 Feb 2012 13:28
by AlexP
Hello,

When assigning value to the TStringField field, if the data size is more than the field length, the data is automatically cut to the needed size in the TStringField.SetAsAnsiString method. If the field type is TWideStringField, the automatic cutting doesn't occur, as such behaviour is not implemented in the given class. When using the TClientDataSet, the data is cutted to the needed length directly in the TCustomClientDataSet.DataConvert method.
We will try to add data size check into the next version of our components.