Преобразование значения параметра в запрос INSERT дает арифметическое переполнение, когда правила отображения BCD активны. - PullRequest
0 голосов
/ 09 мая 2018

Рассмотрим эту таблицу в базе данных MSSQL:

CREATE TABLE dbo.TESTPAR
(
  ID INTEGER NOT NULL,
  YR VARCHAR(50) NULL
)

У меня есть TFDQuery с текстом команды:

insert into TESTPAR
(ID,YR)
values(:ID,cast(:YR as varchar(4)))

Это имеет два ftInteger ptInput параметра

Выполнение с помощью

procedure TFrmCastAsVarchar.BtnTestInsertClick(Sender: TObject);
begin
   Inc(FLastID);
   FDQuery2.Params[0].AsInteger := FLastID;
   FDQuery2.Params[1].AsInteger := 2018;
   try
      FDQuery2.ExecSQL;
   except
      on E:Exception do ShowMessage(E.Message);
   end;
end;

выдает ошибку EMSSQLNativeException Arithmetic overflow converting numeric to data type varchar, когда Mapping для полей dtBCD и dtFmtBCD активен:

procedure TDM.SetBCDMapRules;
// For (Fmt)BCD data types. Called from SetOracleMapRules/SetMSSQLMapRules
begin
   with FDConnection.FormatOptions.MapRules.Add do
   begin      // Convert numeric data types with scale=0 and precision<=10 to a 32-bit integer
      PrecMax := 10;
      PrecMin :=  0;
      ScaleMax := 0;
      ScaleMin := 0;
      SourceDataType := dtBCD;
      TargetDataType := dtInt32;
   end;
   with FDConnection.FormatOptions.MapRules.Add do
   begin      // Do the same for those that might return as dtFmtBCD instead of dtBCD
      PrecMax := 10;
      PrecMin :=  0;
      ScaleMax := 0;
      ScaleMin := 0;
      SourceDataType := dtFmtBCD;
      TargetDataType := dtInt32;
   end;
   with FDConnection.FormatOptions.MapRules.Add do
   begin      // Convert numeric data types with scale=0 and precision>10 to a 64-bit integer
      PrecMin := 11;
      ScaleMax := 0;
      ScaleMin := 0;
      SourceDataType := dtBCD;
      TargetDataType := dtInt64;
   end;
   with FDConnection.FormatOptions.MapRules.Add do
   begin      // Idem dtFmtBCD
      PrecMin := 11;
      ScaleMax := 0;
      ScaleMin := 0;
      SourceDataType := dtFmtBCD;
      TargetDataType := dtInt64;
   end;
   with FDConnection.FormatOptions.MapRules.Add do
   begin      // All other dtBCD types (notably scale <> 0) should return as float
      SourceDataType := dtBCD;
      TargetDataType := dtDouble;
   end;
   with FDConnection.FormatOptions.MapRules.Add do
   begin      // Idem dtFmtBCD
      SourceDataType := dtFmtBCD;
      TargetDataType := dtDouble;
   end;
end;

(Как) я могу изменить SQL, чтобы это исправить?
Кроме того, есть ли в моих правилах отображения что-то странное, что можно исправить? Я удивлен, что это вообще влияет.

  • Это, конечно, только основной пример. Реальный сценарий объединяет другие строки в cast (), чтобы получить значение varchar для помещения в поле varchar.
  • Не с использованием отображений BCD создаст другие проблемы (например, с типами полей DECIMAL).
  • Изменение структуры таблицы для клиента "не оптимально"; -)
  • Я проверил это, используя множество различных ODBC / родных драйверов.
  • Это Delphi Tokyo 10.2.3, приложение Win32 для Win7.

1 Ответ

0 голосов
/ 10 мая 2018

Конечно, что-то не так с вашим отображением (мы были на этом до ).По параметрам это преобразование цели в источник.В теме Data Type Mapping говорится следующее:

В случае параметра команды правило определяет преобразование целевого типа данных, заданногоприложение, в исходный тип данных, поддерживаемый драйвером.

Итак, в этом случае вы указали FireDAC преобразовать 32-разрядное целое число в десятичное число, которое при поступлении в СУБД не будетвсего 4 символа в длину.Если вы хотите это исправить, то (упорядочено по надежности):

  • используйте правильный тип данных в вашей таблице
  • прекратите использовать правила отображения вообще
  • используйте правильныетип данных параметра и значение передачи в том виде, в каком оно есть (например, строка, а не целое число)
  • приводит значение параметра к целому числу, например, CAST(CAST(:YR AS INTEGER) AS VARCHAR(4))
...