Examples

Practical patterns for integrating EgStrParser into database applications.

Basic filter field

The most common usage: an edit field on a search form, validated on exit and applied to a query.

procedure TfrmOrders.edDateExit(Sender: TObject);
var
  R: TParseResult;
begin
  R := ParseInput(edDate.Text, itDate);
  case R.ResultType of
    otEmpty:
      // No filter on this field; remove any existing WHERE clause fragment.
      FDateFilter := '';
    otInvalid:
      begin
        edDate.Color := clYellow;
        Exit;
      end;
  else
    edDate.Color := clWindow;
    FDateFilter := 'OrderDate ' + R.SQLExpression;
  end;
  ApplyFilters;
end;

R.SQLExpression for otEmpty is populated (contains IS NULL or = ''), but the typical application pattern is to treat empty input as “no filter” and remove the field from the WHERE clause entirely.

Range filter

Ranges are detected automatically when the input contains the range separator. No special handling is needed by the caller.

// User types: 100~500
R := ParseInput(edAmount.Text, itDecimal);
// R.ResultType   = otRange
// R.SQLExpression = BETWEEN 100.0000 AND 500.0000

// User types: 2026-01-01~2026-03-31
R := ParseInput(edDate.Text, itDate);
// R.SQLExpression = BETWEEN DATE '2026-01-01' AND DATE '2026-03-31'

Multi-value list

Lists are also detected automatically.

// User types: 1001;1002;1005
R := ParseInput(edIds.Text, itInteger);
// R.ResultType    = otList
// R.SQLExpression = IN (1001, 1002, 1005)

// User types: smith, jones, o'brien
R := ParseInput(edName.Text, itString, [opTrim]);
// R.SQLExpression = IN ('smith', 'jones', 'O''brien')
// User types: acme
R := ParseInput(edCompany.Text, itString, [opTrim], sopLikeBoth);
// R.SQLExpression = LIKE '%acme%' ESCAPE '\'

Query1.SQL.Text := 'SELECT * FROM Companies WHERE CompanyName ' + R.SQLExpression;

Using the component

Configure TEgStrParser at design time (dialect, default operator, default options) and call Parse() from event handlers.

// In the Object Inspector:
//   EgParser1.SqlDialect    = sqlElevateDB
//   EgParser1.DefaultOptions = [opTrim]

procedure TfrmSearch.edNameExit(Sender: TObject);
var
  R: TParseResult;
begin
  R := EgParser1.Parse(edName.Text, itString, sopLikeRight, Sender as TComponent);
  if R.ResultType <> otInvalid then
    FNameFilter := 'CustomerName ' + R.SQLExpression;
end;

Shared handler for multiple fields

procedure TfrmSearch.FilterExit(Sender: TObject);
var
  Ed: TEdit;
  R: TParseResult;
  Fragment: string;
begin
  Ed := Sender as TEdit;

  if Ed = edOrderDate then
    R := EgParser1.Parse(Ed.Text, itDate)
  else if Ed = edAmount then
    R := EgParser1.Parse(Ed.Text, itDecimal)
  else if Ed = edCustomer then
    R := EgParser1.Parse(Ed.Text, itString, [opTrim], sopLikeBoth)
  else
    Exit;

  Ed.Color := IfThen(R.ResultType = otInvalid, clYellow, clWindow);
  if R.ResultType <> otInvalid then
    FFilters.Values[Ed.Name] := R.SQLExpression;

  ApplyFilters;
end;

procedure TfrmSearch.ApplyFilters;
var
  Key, WhereClause: string;
  Parts: TStringList;
begin
  Parts := TStringList.Create;
  try
    for Key in FFilters.Keys do
      if FFilters.Values[Key] <> '' then
        Parts.Add(Key + ' ' + FFilters.Values[Key]);
    WhereClause := Parts.CommaText; // or String.Join(' AND ', ...)
  finally
    Parts.Free;
  end;
  // apply to query...
end;

Handling all result types

procedure ApplyFieldFilter(const ColName, Input: string;
  ValueType: TInputType; Query: TQuery);
var
  R: TParseResult;
begin
  R := ParseInput(Input, ValueType, [opTrim]);
  case R.ResultType of
    otEmpty:
      // No filter: query without a WHERE clause on this column.
      Query.SQL.Text := Format('SELECT * FROM T', []);

    otInvalid:
      raise EValidationError.CreateFmt(
        'Invalid value for %s (error %d)', [ColName, R.ErrorCode]);

  else
    // otSingleValue, otList, otRange -- all produce a valid SQLExpression.
    Query.SQL.Text :=
      Format('SELECT * FROM T WHERE %s %s', [ColName, R.SQLExpression]);
  end;
  Query.Open;
end;

Rejecting empty and NULL

Use opRequired to reject empty input and opNotNull to reject empty list positions.

R := ParseInput(edId.Text, itInteger, [opRequired, opNotNull]);
if R.ErrorCode = ERR_EMPTY_NOT_ALLOWED then
  ShowMessage('ID is required.')
else if R.ErrorCode = ERR_NULL_NOT_ALLOWED then
  ShowMessage('List may not contain empty positions.')
else if R.ResultType = otInvalid then
  ShowMessage('Invalid ID.')
else
  // use R.SQLExpression

Full-text search (ElevateDB)

// User types: payment invoice
R := ParseInput(edKeywords.Text, itString, [opTrim], sopContainsAll);
// sqlElevateDB: CONTAINS ALL 'payment invoice'
// other dialects: LIKE '%payment%invoice%' ESCAPE '\'

Query1.SQL.Text :=
  'SELECT * FROM Documents WHERE Body ' + R.SQLExpression;

Explicit dialect per call (multi-database application)

function BuildFilter(const Input: string; ValueType: TInputType;
  Dialect: TSqlDialect): string;
var
  R: TParseResult;
begin
  R := ParseInput(Input, ValueType, '~', [opTrim], sopEqual, Dialect);
  if R.ResultType in [otSingleValue, otList, otRange] then
    Result := R.SQLExpression
  else
    Result := '';
end;

Locale-aware date input

The parser accepts compact, ISO, and locale date formats without any configuration. Users can type in any of these and receive the same SQL output:

25/03/2026     → DATE '2026-03-25'   (locale dd/mm/yyyy, pt-PT)
2026-03-25     → DATE '2026-03-25'   (ISO)
25032026       → DATE '2026-03-25'   (compact ddmmyyyy)
2503           → DATE '2026-03-25'   (compact ddmm, current year)

No pre-processing is needed on the caller side.

Displaying parsed values

Values[] contains the normalised, locale-aware display strings. Use them to show the user what the parser understood:

R := ParseInput('25032026~31122026', itDate);
// R.Values[0]: '25-03-2026'   (locale pt-PT)
// R.Values[1]: '31-12-2026'
// R.ParsedValue: '25-03-2026 ~ 31-12-2026'

lblParsed.Caption := 'Interpreted as: ' + R.ParsedValue;
(C) 2026 Easygate, Lda