Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Midas/TClientDataSet    [ Add a report in this area ]  
Report #:  1266   Status: Closed
Incorrect filter expression parsing
Project:  Delphi Build #:  6.240
Version:    6.0 Submitted By:   Marcelo Lopez Ruiz
Report Type:  Minor failure / Design problem Date Reported:  5/15/2002 9:07:10 AM
Severity:    Infrequently encountered problem Last Updated: 6/27/2006 12:26:33 PM
Platform:    95, 98, 2000, NT, XP Internal Tracking #:   148825
Resolution: Fixed (Resolution Comments) Resolved in Build: : 7.0.8.1
Duplicate of:  None
Voting and Rating
Overall Rating: (13 Total Ratings)
3.77 out of 5
Total Votes: 7
Description
There appears to be a problem in the filter expression parsing in TClientDataSet. This happens when there is a quote in a value, and the expression parser thinks the rest of the expression is a formula.
Steps to Reproduce:
1. Start a new Delphi project.
2. Place a button on the form, and add the following code to its OnClick event.

procedure TForm1.Button1Click(Sender: TObject);
var
  CDS: TClientDataSet;
  KeyValues: Variant;
begin
  { Add uses Db, DbClient to implementation. }
  CDS := TClientDataSet.Create(nil);
  try
    CDS.FieldDefs.Add('MyField', ftString, 255, False);
    CDS.FieldDefs.Add('MyOtherField', ftString, 255, False);
    KeyValues := VarArrayCreate([0, CDS.FieldDefs.Count - 1], varOleStr);

    // Note that a leading quote will not generate an error.
    // KeyValues[0] := '''Format yyyy-mm-dd';

    // Nor will a simple expression with minuses
    // KeyValues[0] := 'yyyy-mm-dd';

    // Nor will an expression with a single embedded quote
    // KeyValues[0] := 'Format''simple content';

    KeyValues[0] := 'Format''yyyy-mm-dd';
    KeyValues[1] := 'Second value';
    CDS.CreateDataSet;
    CDS.Locate('MyField;MyOtherField', KeyValues, []);
    // Error message: Filter expression incorrectly terminated.
  finally
    CDS.Free;
  end;
end;

3. Add "uses Db, DbClient" to the implementation section.

Another thing that will avoid throwing the exception is not including the second field in the search.
Workarounds
IMPORTANT NOTE: This workaround required modifying the VCL source files. This is usually not recommended, so be very careful. Make a backup of the sources and libraries before modifying anything. Also, make sure that the dbsnap60 run-time package is *not* used, as the contained DBClient unit must be modified.

In file DBClient.pas, function TCustomClientDataSet.LocateRecord make the indicated changes.

      case TField(Fields[i]).DataType of
        ftString, ftFixedChar, ftWideString, ftGUID:
          if (i = Fields.Count - 1) and (loPartialKey in Options) then
            ValStr := QuotedStr(VarToStr(Value) + '*') else (**)(* add this line *)
            ValStr := QuotedStr(VarToStr(Value));           (**)(* add this line *)
            {ValStr := Format('''%s*''',[VarToStr(Value)]) else} (**)(* rem out this line *)
            {ValStr := Format('''%s''',[VarToStr(Value)]);}      (**)(* rem out this line *)


This will correctly escape embedded quotes.

To incorporate the changes into a project, you could include the VCL source path in your project search path, $(delphi)\sources\vcl.

Alternatively, you could follow the intructions found here www.distribucon.com/midasbug/ (2nd paragraph) to compile the changes into the VCL library.

To test the fix against the form built in Steps try out the following.

var
  CDS: TClientDataSet;
  KeyValues: Variant;
begin
  { Add uses Db, DbClient to implementation. }
  CDS := TClientDataSet.Create(nil);
  try
    CDS.FieldDefs.Add('MyField', ftString, 255, False);
    CDS.FieldDefs.Add('MyOtherField', ftString, 255, False);
    KeyValues := VarArrayCreate([0, CDS.FieldDefs.Count - 1], varOleStr);
    KeyValues[1] := 'Second value';
    CDS.CreateDataSet;
    KeyValues[0] := 'Format''yyyy.mm.dd';
    CDS.Locate('MyField;MyOtherField', KeyValues, []);
    KeyValues[0] := '''Format yyyy-mm-dd';
    CDS.Locate('MyField;MyOtherField', KeyValues, []);
    KeyValues[0] := 'yyyy-mm-dd';
    CDS.Locate('MyField;MyOtherField', KeyValues, []);
    KeyValues[0] := 'Format''simple content';
    CDS.Locate('MyField;MyOtherField', KeyValues, []);
    Caption := 'No errors found';
  finally
    CDS.Free;
  end;
end;

---------


In file DBClient.pas, function TCustomClientDataSet.LocateRecord make the indicated changes.

      case TField(Fields[i]).DataType of
        ftString, ftFixedChar, ftWideString, ftGUID:
          if (i = Fields.Count - 1) and (loPartialKey in Options) then
            ValStr := QuotedStr(VarToStr(Value) + '*') else (**)(* add this line *)
            ValStr := QuotedStr(VarToStr(Value));           (**)(* add this line *)
            {ValStr := Format('''%s*''',[VarToStr(Value)]) else} (**)(* rem out this line *)
            {ValStr := Format('''%s''',[VarToStr(Value)]);}      (**)(* rem out this line *)
Attachment
None
Comments

Craig Stuntz at 10/3/2002 8:07:27 AM -
Dave, have you verified that this is still in D7?

Dave Rowntree at 10/3/2002 9:23:56 AM -
Yes, and the problem still exists in D7 Craig

Pawel Cichocki at 9/1/2003 4:45:17 AM -
Why?
Why method Locate use filtering?
There are faster and easier ways... (look at JVCL library; TJvMemoryData code)

Server Response from: CODE1