# знаки в локациях ADO (Delphi XE5)

С TADOQuery.Locate, который использует список полей и VarArray значений, если одно из значений содержит знак #, мы получаем это исключение:

'Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.'

Я проследил это до ADODB, который, кажется, сам использует знаки # в качестве разделителей.

Есть ли способ избежать #-символов, чтобы запрос не потерпел неудачу?

* РЕДАКТИРОВАТЬ 1 *

Я был неправ. Причиной этого сбоя является строка со знаком фунта и одинарной кавычкой. Показанный ниже код завершается ошибкой с сообщением об ошибке, отмеченным выше.

Что нас действительно беспокоит, так это то, что когда он не запускается как.exe вне IDE, не возникает исключение времени выполнения. Мы видим исключение, только когда находимся в IDE. Если бы наши программисты не использовали данные, вызывающие это, мы бы никогда не узнали, что.Locate вернул FALSE из-за ошибки времени выполнения, а не потому, что соответствующая запись не была найдена.

Код:

var 
  SearchArray: Variant;
begin
  SearchArray := VarArrayCreate([0,1], VarVariant);
  SearchArray[0] := 'T#more''wo';
  SearchArray[1] := 'One';

  ADOQuery.Locate('FieldName1;FieldName2', SearchArray, []);

1 ответ

Решение

Пожалуйста, смотрите Обновление ниже; Я нашел обходной путь, который по крайней мере стоит протестировать.

Даже с таблицами Sql Server, # не должен быть спасен

Следующий код работает правильно в D7..XE8

procedure TForm1.Button1Click(Sender: TObject);
begin
  AdoQuery1.Locate('country;class', VarArrayOf(['GB', Edit1.Text]), []);
end;

когда Edit1.Text содержит "D#E", поэтому я думаю, что ваша проблема должна лежать в другом месте. Попробуйте минималистский проект только с этим кодом, после перезагрузки вашей машины.

Обновление: как отмечено в комментарии, есть проблема с .Locate где выражение передано GetFilterStr (в ADODB.Pas) содержит # сопровождаемый единственной цитатой. Чтобы попытаться обойти это, я пересадил GetFilterStr в мой код и экспериментировал с его использованием для создания фильтра набора записей в моем AdoQuery, так как я заметил, что это то, что .Locate делает в заявлении

FLookupCursor.Filter := LocateFilter;

Код, который я использую для этого, включая мою "исправленную" версию GetFilterStr, ниже.

Что мне пока не удалось выяснить, так это как избежать исключения из

    AdoQuery1.Recordset.Filter := S;

когда выражение фильтра не дает записей.

(Кстати, для удобства я делаю это в D7, но использую XE8 GetFilterStrВот почему я должен был закомментировать ссылку на ftFixedWideChar)

function GetFilterStr(Field: TField; Value: Variant; Partial: Boolean = False): WideString;
// From XE8 Data.Win.ADODB
var
  Operator,
  FieldName,
  QuoteCh: WideString;
begin
  QuoteCh := '';
  Operator := '=';
  FieldName := Field.FieldName;
  if Pos(' ', FieldName) > 0 then
    FieldName := WideFormat('[%s]', [FieldName]);
  if VarIsNull(Value) or VarIsClear(Value) then
    Value := 'Null'
  else
    case Field.DataType of
      ftDate, ftTime, ftDateTime:
        QuoteCh := '#';
      ftString, ftFixedChar, ftWideString://, ftFixedWideChar:
        begin
          if Partial and (Value <> '') then
          begin
            Value := Value + '*';
            Operator := ' like ';     { Do not localize }
          end;
{.$define UseOriginal}
{$ifdef UseOriginal}
          if Pos('''', Value) > 0 then
            QuoteCh := '#' else
            QuoteCh := '''';
{$else}
          QuoteCh := '''';
          if Pos('''', Value) > 0 then begin
            QuoteCh := '';
            Value := QuotedStr(Value);
          end;
{$endif}
        end;
    end;
  Result := WideFormat('(%s%s%s%s%2:s)', [FieldName, Operator, QuoteCh, VarToWideStr(Value)]);
end;

procedure TForm1.CreateFilterExpr;
var
  S : String;
begin
  // clear any existing filter
  AdoQuery1.Recordset.Filter := adFilterNone;
  AdoQuery1.Refresh;

  if edFilter.Text = '' then Exit;

  S := GetFilterStr(AdoQuery1.FieldByName('Applicant'), edFilter.Text, cbPartialKey.Checked);
  //  Add the filter expr to Memo1 so we can inspect it
  Memo1.Lines.Add(S);
  try
    AdoQuery1.Recordset.Filter := S;
    AdoQuery1.Refresh;
  except
  end;
end;

procedure TForm1.FilterClick(Sender: TObject);
begin
  CreateFilterExpr;
end;

Обновление 2: попробуйте следующее:

  • Скопируйте Data.Win.ADODB.Pas в каталог вашего проекта

  • В нем замените GetFilterExpr на приведенную выше версию, убедившись, что UseOriginal не DEFINEd и что ftFixedWideChar восстановлен в операторе Case.

  • Создай и запусти свой проект

Во всяком случае, в XE8 мой испытательный стенд теперь корректно отображает поле Locate(), оканчивающееся на ' или же #'(или содержащие любой из них, если loPartialKey указан. (Я не могу тестировать в XE4/5, потому что мой XE4 теперь говорит, что он не лицензирован, так как я обновился до Win10 на прошлой неделе, спасибо EMBA!)

Я пока не решаюсь назвать это решением или даже обходным путем, но это, по крайней мере, стоит проверить. Я не уверен, что я бы назвал оригинальную версию GetFilterExpr прослушивается, потому что я не уверен, какой вариант использования предназначен для обработки значений, содержащих кавычки.

Другие вопросы по тегам