Delphi Firedac Oracle: вызывает исключение при обнаружении первичного ключа (VARCHAR или VARCHAR2)

пример таблицы для оракула

   create table appval (
     id varchar2(4) not null primary key,
     val1 number(38,0) default 0,
     val2 number(5,2) default 0);

   insert into appval (id,val1) values ('1101', 1500000);
   insert into appval (id,val2) values ('1102', 2.5);

пример delphi (Rad Studio)

type 
  TValHelper = class (TComponent)
  private
    FDataSet: TFDTable;
  protected
    procedure PrepareTable;
  public 
    constructor CreateHelper(AOwner: TComponent; var ATable: TFDTable); reintroduce;
    function GetVal1(AId: string): Currency;
    function GetVal2(AId: string): Double;
  end;

constructor CreateHelper(AOwner: TComponent; var ATable: TFDTable);
begin
  inherited Create(AOwner);
  if not Assigned(ATable) then
     Raise Exception.Create('Null Parameter passed.');

  FDataSet := ATable;
  PrepareTable;
end;

procedure TValHelper.PrepareTable;
begin
  if not FDataSet.Active then
  begin
    FDataSet.Connection := Dm.FDConnection;
    FDataSet.TableName := 'appval';
    FDataSet.Open;
  end;
end;

function TValHelper.GetVal1 (AId: string): Currency;
begin
  Result := 0; {default value if false}
  if AppVal.Locate('id', AId, 
    [loCaseInsensitive, loPartialKey]) then {<-- Exception}
    Result := AppNumVal.Fields[1].AsCurrency;
end;

function TValHelper.GetVal2 (AId: string): Double;
begin
  Result := 0; {default value if false}
  if AppVal.Locate('id', AId, 
    [loCaseInsensitive, loPartialKey]) then {<-- Exception}
    Result := AppNumVal.Fields[2].AsFloat;
end;

когда я вызываю внутреннюю функцию GetVal1 «Найти», возникает исключение с сообщением

Exception class EFDException with message '[FireDAC][Phys][Ora]-345. 
Data too large for variable 
[:FD__LC_ID]. Max len = [4], actual len = [5] Hint: 
set the TFDParam.Size to a greater value'.

я пытаюсь воспроизвести этот тест с другой БД (SQLite) без исключения, как это делает оракул.

это была ошибка или чего-то не хватало в моем коде. может кто-нибудь объяснить.

я делаю исследование и пытаюсь отследить эту ошибку, я попадаю в подозрительный блок в FireDac.Comp.Client.pas (Delphi Tokyo 10.2)

{LINE 12690}
procedure TFDTable.FetchWindow
begin 
  {some code}
  {line 12769 i Set Watch at Command.CommandText.Text}
  Command.CommandText.Add(GenerateSQL);
  {Watch value : 
       'SELECT A.*, A.ROWID AS FD__ROWID'
       'FROM APPVAL A'
       'WHERE ({FN UCASE(A.ID)} LIKE {FN UCASE(:FD__LC_ID)})'
       'ORDER BY A.ID ASC'
       '{LIMIT(1)}'
     :F__LC_ID <-- is param with size 4

     until this
     everything goes fine.
  } 

  // check locate params {LINE 12785}
  else if IsPrefixed(oParam.Name, C_FD_CmdGenLocate, sField) then begin
    oParam.AssignFieldValue(FieldByName(sField), GetLocateRow.ValueS[sField]);
    // made LIKE compatible String
    if FTableParams.FLocatePartial and
       (oParam.DataType in [ftString, ftWideString, ftFixedChar, ftFixedWideChar]) then
      oParam.Value := VarToStr(oParam.Value) + '%'; {LINE 12791} 
      {
         something wrong with this oParam.Value Plus 1 char '%' 
         and i assume field has only 4 so if we increase the field size 
         manualy value will be padded with space before '%' so this will
         still produce exception (oParam.Value always greater +1).
      }
  end

end;

как я могу решить это.


person Dicky Tamara    schedule 23.08.2017    source источник


Ответы (1)


Мы можем назвать это ошибкой. FireDAC может в случае добавления символа % увеличить предельный размер параметра. Однако в вашем случае кажется, что вы хотите найти запись по точному термину, а не по частичному. Если это так, просто исключите loPartialKey из вашего Locate< /a> вызов метода. Если нет, вы можете самостоятельно обрезать поисковый запрос на один символ в качестве быстрого обходного пути.

person Victoria    schedule 23.08.2017
comment
да, очень грязно менять базу firedac. у меня удалить loPartialKey может быть хуже всего с другим кодом при моей разработке. увеличьте мой код и работайте над моим случаем, мы переходим с DB2 на Oracle. спасибо. - person Dicky Tamara; 23.08.2017
comment
Что ж, решение за вами. Но бери как есть. Если у вас есть значения поля длиной 4 символа и поиск по 4 символам в долгосрочной перспективе, он не может быть частичным, но точным. Частичный поиск будет выполняться для терминов короче 4 символов. - person Victoria; 23.08.2017
comment
По своей природе Firedac использует вторичную таблицу для поиска, и перед тем, как создать вторичную таблицу (временную), они проверяют статус выборки, если все извлечено, затем поиск переходит в механизм памяти, но каким-то образом в моем случае он направляется непосредственно в СУБД, чтобы сначала попытаться найти в удаленном режиме. поэтому, когда он обращается напрямую, длина строки может вызвать проблему, потому что они помещают «%» в параметр как значение UCASE (‹-- для loCaseInsensitive) (: FD__LC_ID% (‹--lopartial ключ)) максимальная длина для loPartialKey должна быть -1, между прочим Я действительно ценю ваше предложение. - person Dicky Tamara; 23.08.2017
comment
Может быть if Length(oParam.Value) = oParam.Size then oParam.Size := oParam.Size + 1, который увеличит размер параметра при выполнении поиска термина по длине максимального размера поля, но опять же, если вы ищете термин, длина которого равна максимальной длине поля, это не частичный, а точный поиск. - person Victoria; 23.08.2017
comment
да, вы правы, проверяя значение для oParam, но меня беспокоит, почему EMBT помещает оператор sql, например «%», в значение oParam, а не в функцию GenerateSql как полный оператор sql, когда они помещают «%» в качестве значения, когда мы пользователь передаем %, чтобы найти его автоматически запускать PartialKey (мое предположение) даже наихудшую инъекцию или что-то связанное с безопасностью. в firedac они помещают sql, а «%» может вызвать много проблем для дальнейшего или для данных, превышающих размер. - person Dicky Tamara; 23.08.2017
comment
Вы правы, символ % мог быть добавлен в метод GenerateSelectTable (у меня более старая версия FireDAC, поэтому, возможно, у вас разные имена методов, но я вижу аналогичный код, который вы опубликовали). Однако его нельзя вводить, потому что команда по-прежнему использует параметры. Что касается команды SQL, для частичной локации лучше проверить, соответствует ли значение параметра длине размера столбца, и если да, то использовать = вместо LIKE (это будет более эффективно для СУБД). И прежде чем все это начнется, может быть проверка на случай, если поисковый запрос длиннее размера поля. - person Victoria; 23.08.2017
comment
Рад слышать безопасный, мой пример таблицы SQLite для сравнения я передаю неточное значение ‹-- Headace в течение 3 часов, так как мое предположение, когда мы заполнили до максимума или нет в loPartialKey, вернет часть строки, поэтому я думаю, что EMBT не хочет «Мы живем с лучшим утром завтра». - person Dicky Tamara; 23.08.2017