# знаки в локациях 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
прослушивается, потому что я не уверен, какой вариант использования предназначен для обработки значений, содержащих кавычки.