Ошибка Parameter.AsString в Oracle/MSSQL — 2-байтовые символы Parameter.Value в Oracle

После перехода на FireDAC у меня возникли проблемы с тем, чтобы этот код работал на MSSQL/Oracle:

with DataFormsettings do
begin
  Close;
  if Params.Count=0 then FetchParams;
  Params.ParamByName('TT_EMP_ID').Asinteger := AEmpID;
  Params.ParamByName('TT_FORM').AString := UpperCase(AKey);  
  Open;
  if (RecordCount>0) then
     S := FieldByName('TT_VIEWDATA').Asstring;     
end;   

AKey и S являются строками.

Оператор Open выдает ошибку

[FireDAC][Phys][MSSQL]-338 Param type changed from [ftString] to [ftWidestring]
[FireDAC][Phys][Ora]-338 Param type changed from [ftString] to [ftWidestring]

при подключении к базе данных MSSQL или Oracle; не при подключении к FireBird.
После FetchParams DataFormsettings.params[1].datatype всегда стоит ftString.

Если я заменю

Params.ParamByName('TT_FORM').AString := UpperCase(AKey);  

с

Params.ParamByName('TT_FORM').Value := UpperCase(AKey);

... в операторе Open нет ошибок. Я думал, что решил это, хотя я не очень понял ошибку. В конце концов, это должны быть все типы Delphi String по умолчанию...
Но теперь присваивание S не работает для Oracle (не FireBird или MSSQL) в том смысле, что я вижу, что возвращаются 2-байтовые символы. С содержит:

\'#0'S'#0'o'#0'f'#0't'#0'w'#0'a'#0'r'#0'e'#0'\'#0'T'#0'i'#0'm'#0'e'#0'T'#0'e'#0'l'#0'l'#0'...

Я могу справиться с этим, например.

S := TEncoding.Unicode.GetString(FieldByName('TT_VIEWDATA').AsBytes);  

для Oracle, но (конечно) при использовании двух других типов баз данных это не работает:

No mapping for the Unicode character exists in the target multi-byte code page

Что мне здесь не хватает? В частности, я хотел бы просто заставить работать извлечение/назначение AsString.
Обратите внимание, что Установка свойства AsString устанавливает для свойства DataType значение ftWideString или ftString в примечании документация FireDAC TFDParam.AsString. Кажется, что присваивание значения параметра просто переключает тип с ftString на ftWideString (на что указывает исходная ошибка).

DataFormSettings — это TClientDataSet в клиентском приложении, подключенном к серверному приложению, где находятся TDataSetProvider и TFDQuery. Запрос

select
  TT_FORMSETTINGS_ID,
  TT_EMP_ID,
  TT_FORM,
  TT_VERSION,
  TT_VIEWDATA
from TT_FORMSETTINGS
where TT_EMP_ID=:TT_EMP_ID
and TT_FORM=:TT_FORM

Таблицы были созданы следующим образом:

Огненная птица:

CREATE TABLE TT_FORMSETTINGS
(
  TT_FORMSETTINGS_ID    INTEGER DEFAULT 0 NOT NULL,
  TT_EMP_ID     INTEGER,
  TT_FORM       VARCHAR(50),
  TT_VERSION        INTEGER,
  TT_VIEWDATA       BLOB SUB_TYPE TEXT SEGMENT SIZE 80,
  TT_TAG    INTEGER,
  TT_TAGTYPE    INTEGER,
  TT_TAGDATE    TIMESTAMP
);

Оракул:

CREATE TABLE TT_FORMSETTINGS
(
  TT_FORMSETTINGS_ID    NUMBER(10,0) DEFAULT 0 NOT NULL,
  TT_EMP_ID     NUMBER(10,0),
  TT_FORM       VARCHAR(50),
  TT_VERSION        NUMBER(10,0),
  TT_VIEWDATA       CLOB,
  TT_TAG    NUMBER(10,0),
  TT_TAGTYPE    NUMBER(10,0),
  TT_TAGDATE    DATE
);

MSSQL:

CREATE TABLE TT_FORMSETTINGS
(
  TT_FORMSETTINGS_ID    INTEGER  NOT NULL CONSTRAINT TT_C0_FORMSETTINGS DEFAULT 0,
  TT_EMP_ID     INTEGER NULL,
  TT_FORM       VARCHAR(50) NULL,
  TT_VERSION        INTEGER NULL,
  TT_VIEWDATA       TEXT NULL,
  TT_TAG    INTEGER NULL,
  TT_TAGTYPE    INTEGER NULL,
  TT_TAGDATE    DATETIME NULL
);

Я проверил, что TT_VIEWDATA содержит правильные данные во всех базах данных; это длинная строка, содержащая CRLF:

\Software\TimeTell\Demo8\Forms\TFormTileMenu'#$D#$A'Version,1,80502'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu\FormTileMenu.TileControlMenu'#$D#$A'Version,4,2'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu\FormTileMenu.TileControlMenu...

Примечания:

  • В настоящее время тестируется на SQL Server 2008 и Oracle 10, но я ожидаю, что это не будет отличаться для других версий.
  • FWIW, select * from NLS_database_PARAMETERS where parameter like '%CHARACTERSET%' возвращает NLS_CHARACTERSET=WE8MSWIN1252 и NLS_NCHAR_CHARACTERSET=AL16UTF16
    Запрос SELECT dump(dbms_lob.substr(tt_viewdata,100,1), 1016), tt_viewdata FROM tt_formsettings подтверждает, что CLOB содержит байты ASCII для кодовой страницы Win1252:
    Typ=1 Len=100 CharacterSet=WE8MSWIN1252: 5c,53,6f,66,74,77,61,72,65,5c,54,69,6d,65,54,65,6c,6c,5c,44,65,...
  • FieldByName().AsANSIString дает те же результаты, что и FieldByName().AsString

Дополнительная информация: это устаревшее приложение с постоянными определениями полей в файле DataFormsettings TClientDataset. TT_VIEWDATA определяется как TMemoField:

DataFormsettingsTT_VIEWDATA: TMemoField;

В небольшом тестовом приложении (напрямую подключенном к Oracle, а не клиент-серверном) я позволил Delphi добавить определения полей, а затем он сказал:

DataFormsettingsTT_VIEWDATA: TWideMemoField;

Если я использую это в основном приложении, Oracle работает нормально, но тогда я получаю «мусор» для MSSQL.

Я также экспериментировал с настройкой правил сопоставления для соединения с Oracle, например (много вариантов):

with AConnection.FormatOptions.MapRules.Add do
begin
  SourceDataType := dtWideMemo;
  TargetDataType := dtMemo;
end;
AConnection.FormatOptions.OwnMapRules := true;

но это не помогло.


person Jan Doggen    schedule 06.12.2017    source источник
comment
Зачем вы описываете параметры (вашим FetchParams методом)? Это произошло так же, как здесь (см. среднюю часть поста). Бьюсь об заклад, если вы уберете вызов FetchParams, у вас не будет никаких проблем. FireDAC автоматически преобразует значения параметров для подготовленного оператора. Но он не позволяет подготовить оператор (описывающий параметры), изменить типы данных параметров и выполнить оператор.   -  person Victoria    schedule 06.12.2017
comment
@Виктория?? Если я не выполняю FetchParams, параметров не будет, и назначения Params.ParamByName немедленно завершатся ошибкой. Это клиент-серверное приложение с TClientDataSet на клиенте и TFDQuery/TDataSetProvider на сервере. Как еще клиент узнает параметры?   -  person Jan Doggen    schedule 07.12.2017
comment
FireDAC анализирует запрос и создает объекты параметров для маркеров в запросе (по умолчанию). Я не знаю вашей установки. Из вашего вопроса я просто предположил, что DataFormsettings является TFDQuery и что вы запускаете команды, которые вы показали.   -  person Victoria    schedule 07.12.2017
comment
Извините, обновил текст моего вопроса, чтобы сказать, что DataFormsettings - это тот TClientDataSet, который упоминается позже.   -  person Jan Doggen    schedule 07.12.2017
comment
Насколько я понимаю, и я не уверен, что это так для FireDAC, varchar(50) (по крайней мере, для MSSQL) не является юникодом. string на стороне Delphi является юникодом, поэтому предполагается, что если доступ к параметру осуществляется с помощью AsString, используется юникод, тип данных установлен на ftWideString. Например. в Data.DB: procedure TParam.SetAsString(const Value: string); begin if FDataType <> ftFixedWideChar then FDataType := ftWideString; Self.Value := Value; end;. Имеет ли это смысл? Вам действительно нужно иметь varchar полей?   -  person nil    schedule 07.12.2017
comment
@nil Да, мне нужно иметь поля varchar - разве они не являются наиболее «распространенными» для MSSQL в устаревшем приложении?   -  person Jan Doggen    schedule 11.12.2017
comment
Ну, может быть, я не могу сказать, так как мы вместе переключили интерфейс и сервер на юникод. Я хотел сказать, что вы обращаетесь к полям и параметрам так, как если бы они представляли то, что является родным string в Delphi, но на самом деле это не так, поскольку они не являются юникодными. Я чувствую, что вы смешиваете здесь юникод и не юникод. Используемые вами поля и параметры поддерживают AsAnsiString?   -  person nil    schedule 11.12.2017


Ответы (2)


Вот почему это не работает:

In FireDAC.Stan.Option:

procedure TFDFormatOptions.ColumnDef2FieldDef()
...
dtWideHMemo:
  // Here was ftOraClob, but then will be created TMemoField,
  // which does not know anything about Unicode. So, I have
  // changed to ftFmtMemo. But probably may be problems ...
  ADestFieldType := ftWideMemo;

Действительно, вероятно, могут быть проблемы.

Решение состоит в том, чтобы добавить правило сопоставления, которое преобразует dtWideHMemo в dtMemo.
После этого чтение и запись в CLOB .AsString работают нормально.

Отмечено как RSP-19600 на портале качества Embarcadero.


Для полноты: поскольку сопоставление, упомянутое в моем другом ответе, больше не активно, вам нужно изменить доступ к параметрам с помощью .Value вместо .AsString.

person Jan Doggen    schedule 20.12.2017

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

Произошли две вещи, и каждую из них можно обойти с помощью следующих изменений:

  1. Ошибка Изменен тип параметра при назначении значения параметра
  2. Определения полей и FieldByName().AsString поиск/назначение не работают

Обратите внимание, что я ограничен определениями полей во время разработки во всем приложении, которое должно обрабатывать все три типа базы данных, в частности, постоянное поле DataFormSettingsTT_VIEWDATA является полем TMemoField.

С определениями таблиц, упомянутыми в нижней части вопроса, если вы настроите TFDConnection -> TFDQuery -> TDataSetProvider -> TClientDataSet и добавите определения полей с помощью Добавить все поля, DataFormSettingsTT_VIEWDATA будет иметь тип:

  • TMemoField с BlobType=ftMemoField для FireBird

  • TMemoField с BlobType=ftWideMemoField для MSSQL

  • TWideMemoField с BlobType=ftWideMemoField для Oracle.

Ручное редактирование .DFM и .PAS для возврата Oracle TWideMemoField к TMemoField работает (ну, мне не нужно его менять, это устаревший код), если я также:

  • принудительно BlobType=ftWideMemoField для времени разработки TMemoFields во время выполнения (я могу сделать это в OnCreate в родительском элементе, от которого происходят все мои модули данных);

  • обрабатывать поиск строки только для Oracle как TEncoding.Unicode.GetString(FieldByName(SFormSettingsViewData).AsBytes).

Но это все равно не оптимально. Мой клиентский код с TClientDataSet теперь нужно будет знать, что это за база данных. У меня есть средства в клиентском приложении, чтобы запросить это у сервера.

Вот пример приложения с этими изменениями:

uFireDacOracleBlob.pas файл:

unit uFireDacOracleBlob;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def,
  FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.Oracle,
  FireDAC.Phys.OracleDef, FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS,
  FireDAC.DApt.Intf, FireDAC.DApt, Datasnap.DBClient, Datasnap.Provider,
  Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client, Vcl.StdCtrls, Vcl.ExtCtrls,
  FireDAC.Phys.MSSQL, FireDAC.Phys.MSSQLDef, FireDAC.Phys.IB,
  FireDAC.Phys.IBDef, FireDAC.Phys.FBDef, FireDAC.Phys.IBBase, FireDAC.Phys.FB,
  FireDAC.Phys.ODBCBase;

type
  TFrmFireDacOracleBlob = class(TForm)
    FDConnection1: TFDConnection;
    FDPhysOracleDriverLink1: TFDPhysOracleDriverLink;
    FDQuery1: TFDQuery;
    DataSetProvider1: TDataSetProvider;
    ClientDataSet1: TClientDataSet;
    Edit0: TEdit;
    Label1: TLabel;
    LblPos0: TLabel;
    RGpDB: TRadioGroup;
    BtnOpen: TButton;
    FDConnection2: TFDConnection;
    FDQuery2: TFDQuery;
    DataSetProvider2: TDataSetProvider;
    ClientDataSet2: TClientDataSet;
    FDConnection0: TFDConnection;
    FDQuery0: TFDQuery;
    DataSetProvider0: TDataSetProvider;
    ClientDataSet0: TClientDataSet;
    FDPhysMSSQLDriverLink1: TFDPhysMSSQLDriverLink;
    FDPhysFBDriverLink1: TFDPhysFBDriverLink;
    ClientDataSet0TT_FORMSETTINGS_ID: TIntegerField;
    ClientDataSet0TT_EMP_ID: TIntegerField;
    ClientDataSet0TT_FORM: TStringField;
    ClientDataSet0TT_VERSION: TIntegerField;
    ClientDataSet0TT_VIEWDATA: TMemoField;

    ClientDataSet1TT_FORMSETTINGS_ID: TIntegerField;
    ClientDataSet1TT_EMP_ID: TIntegerField;
    ClientDataSet1TT_FORM: TStringField;
    ClientDataSet1TT_VERSION: TIntegerField;
    ClientDataSet1TT_VIEWDATA: TMemoField;

    ClientDataSet2TT_FORMSETTINGS_ID: TIntegerField;
    ClientDataSet2TT_EMP_ID: TIntegerField;
    ClientDataSet2TT_FORM: TStringField;
    ClientDataSet2TT_VERSION: TIntegerField;
    ClientDataSet2TT_VIEWDATA: TMemoField;
    BtnSet: TButton;
    Label2: TLabel;
    LblPos1: TLabel;
    Edit1: TEdit;
    Label4: TLabel;
    LblPos2: TLabel;
    Edit2: TEdit;
    BtnParam: TButton;
    procedure BtnOpenClick(Sender: TObject);
    procedure BtnSetClick(Sender: TObject);
    procedure BtnParamClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FStrFirebird,
    FStrOracle,
    FStrMSSQL   :String;
    procedure ShowString(AStr: String; ALbl: TLabel; AEdit: TEdit);
  public
  end;

var
  FrmFireDacOracleBlob: TFrmFireDacOracleBlob;

implementation

{$R *.dfm}

const
   cSQLText = 'select TT_FORMSETTINGS_ID,TT_EMP_ID,TT_FORM,TT_VERSION,TT_VIEWDATA from TT_FORMSETTINGS where TT_EMP_ID=:TT_EMP_ID and TT_FORM=:TT_FORM';

procedure TFrmFireDacOracleBlob.BtnParamClick(Sender: TObject);
begin
  case RGpDB.ItemIndex of
     0: begin
           FDQuery0.SQL.Text := cSQLText;
           with ClientDataSet0 do
           begin
              if Params.Count=0 then FetchParams;
              Params.ParamByName('TT_EMP_ID').Asinteger := 1;
              Params.ParamByName('TT_FORM').AsString := 'TFORMTILEMENU';
              Open;
              if (RecordCount>0) then
                 FStrFirebird := FieldByName('TT_VIEWDATA').Asstring;
              ShowString(FStrFireBird,LblPos0,Edit0);
           end;
        end;
     1: begin
           FDQuery1.SQL.Text := cSQLText;
           with ClientDataSet1 do
           begin
              if Params.Count=0 then FetchParams;
              Params.ParamByName('TT_EMP_ID').Asinteger := 1;
              Params.ParamByName('TT_FORM').AsString := 'TFORMTILEMENU';
              Open;
              if (RecordCount>0) then
                 // FStrOracle := FieldByName('TT_VIEWDATA').Value;
                 FStrOracle := TEncoding.Unicode.GetString(FieldByName('tt_viewdata').AsBytes);
              ShowString(FStrOracle,LblPos1,Edit1);
           end;
        end;
     2: begin
           FDQuery2.SQL.Text := cSQLText;
           with ClientDataSet2 do
           begin
              if Params.Count=0 then FetchParams;
              Params.ParamByName('TT_EMP_ID').Asinteger := 1;
              Params.ParamByName('TT_FORM').AsString := 'TFORMTILEMENU';
              Open;
              if (RecordCount>0) then
                 FStrMSSQL := FieldByName('TT_VIEWDATA').Asstring;
              ShowString(FStrMSSQL,LblPos2,Edit2);
           end;
        end;
  end;
end;

procedure TFrmFireDacOracleBlob.BtnSetClick(Sender: TObject);
begin
  case RGpDB.ItemIndex of
     0: begin
           FStrFirebird := FStrFirebird + #13#10'Added another line';
           ClientDataSet0.Edit;
           ClientDataSet0.FieldByName('tt_viewdata').Value := FStrFireBird;
           ClientDataSet0.ApplyUpdates(0);
        end;
     1: begin
           FStrOracle := FStrOracle + #13#10'Added another line';
           ClientDataSet1.Edit;
           // ClientDataSet1.FieldByName('tt_viewdata').AsString := FStrOracle; // does not work
           // ClientDataSet1.FieldByName('tt_viewdata').Value := FStrOracle;    // does not work
           ClientDataSet1.FieldByName('tt_viewdata').Value := TEncoding.Unicode.GetBytes(FStrOracle);
           // ClientDataSet1.FieldByName('tt_viewdata').AsBytes := TEncoding.Unicode.GetBytes(FStrOracle);  Also works
           ClientDataSet1.ApplyUpdates(0);
        end;
     2: begin
           FStrMSSQL := FStrMSSQL + #13#10'Added another line';
           ClientDataSet2.Edit;
           ClientDataSet2.FieldByName('tt_viewdata').AsString := FStrFireBird;
           ClientDataSet2.ApplyUpdates(0);
        end;
  end;
end;

procedure TFrmFireDacOracleBlob.FormCreate(Sender: TObject);
var i: integer;
begin
   for i := 0 to self.ComponentCount-1 do
      if (self.Components[i] is TMemoField) then
         (self.Components[i] as TMemoField).BlobType := ftWideMemo;
end;

procedure TFrmFireDacOracleBlob.ShowString(AStr: String; ALbl: TLabel; AEdit: TEdit);
begin
  ALbl.Caption := IntToStr(Pos(#13#10,AStr));
  AEdit.Text := AStr;
end;

procedure TFrmFireDacOracleBlob.BtnOpenClick(Sender: TObject);
begin
  case RGpDB.ItemIndex of
     0: begin
           // SetFireBirdMapRules(FDConnection1);   Design time
           ClientDataSet0.Open;
           FStrFirebird := ClientDataSet0.FieldByName('tt_viewdata').AsString;
           ShowString(FStrFireBird,LblPos0,Edit0);
        end;
     1: begin
           // SetOracleMapRules(FDConnection1);   Design time
           ClientDataSet1.Open;
           // FStrOracle := ClientDataSet1.FieldByName('tt_viewdata').AsString;
           FStrOracle := TEncoding.Unicode.GetString(ClientDataSet1.FieldByName('tt_viewdata').AsBytes);
           ShowString(FStrOracle,LblPos1,Edit1);
        end;
     2: begin
           // SetMSSQLMapRules(FDConnection1);   Design time
           ClientDataSet2.Open;
           FStrMSSQL := ClientDataSet2.FieldByName('tt_viewdata').AsString;
           ShowString(FStrMSSQL,LblPos2,Edit2);
        end;
  end;
end;

end.

uFireDacOracleBlob.dfm файл:

object FrmFireDacOracleBlob: TFrmFireDacOracleBlob
  Left = 0
  Top = 0
  Caption = 'FireDac and Oracle Clobs'
  ClientHeight = 278
  ClientWidth = 577
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  Position = poScreenCenter
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 32
    Top = 161
    Width = 91
    Height = 13
    Caption = 'Position first CRLF:'
  end
  object LblPos0: TLabel
    Left = 128
    Top = 161
    Width = 6
    Height = 13
    Caption = '0'
  end
  object Label2: TLabel
    Left = 32
    Top = 203
    Width = 91
    Height = 13
    Caption = 'Position first CRLF:'
  end
  object LblPos1: TLabel
    Left = 128
    Top = 203
    Width = 6
    Height = 13
    Caption = '0'
  end
  object Label4: TLabel
    Left = 32
    Top = 245
    Width = 91
    Height = 13
    Caption = 'Position first CRLF:'
  end
  object LblPos2: TLabel
    Left = 128
    Top = 245
    Width = 6
    Height = 13
    Caption = '0'
  end
  object Edit0: TEdit
    Left = 32
    Top = 138
    Width = 505
    Height = 21
    TabOrder = 0
  end
  object RGpDB: TRadioGroup
    Left = 32
    Top = 8
    Width = 249
    Height = 33
    Columns = 3
    ItemIndex = 0
    Items.Strings = (
      'FireBird'
      'Oracle'
      'MSSQL')
    TabOrder = 1
  end
  object BtnOpen: TButton
    Left = 32
    Top = 56
    Width = 75
    Height = 25
    Caption = 'Open Table'
    TabOrder = 2
    OnClick = BtnOpenClick
  end
  object BtnSet: TButton
    Left = 120
    Top = 56
    Width = 75
    Height = 25
    Caption = 'Update field'
    TabOrder = 3
    OnClick = BtnSetClick
  end
  object Edit1: TEdit
    Left = 32
    Top = 180
    Width = 505
    Height = 21
    TabOrder = 4
  end
  object Edit2: TEdit
    Left = 32
    Top = 222
    Width = 505
    Height = 21
    TabOrder = 5
  end
  object BtnParam: TButton
    Left = 32
    Top = 96
    Width = 104
    Height = 25
    Caption = 'Open with params'
    TabOrder = 6
    OnClick = BtnParamClick
  end
  object FDConnection1: TFDConnection
    Params.Strings = (
      'User_Name=testv4'
      'Password=testv4'
      'Database=VS2003-2005-10'
      'DriverID=Ora')
    FormatOptions.AssignedValues = [fvMapRules]
    FormatOptions.OwnMapRules = True
    FormatOptions.MapRules = <
      item
        SourceDataType = dtBCD
        TargetDataType = dtInt32
      end
      item
        SourceDataType = dtFmtBCD
        TargetDataType = dtDouble
      end>
    Connected = True
    LoginPrompt = False
    Left = 312
    Top = 72
  end
  object FDPhysOracleDriverLink1: TFDPhysOracleDriverLink
    Left = 368
    Top = 72
  end
  object FDQuery1: TFDQuery
    Connection = FDConnection1
    SQL.Strings = (
      'select * from tt_formsettings')
    Left = 416
    Top = 72
  end
  object DataSetProvider1: TDataSetProvider
    DataSet = FDQuery1
    Left = 464
    Top = 72
  end
  object ClientDataSet1: TClientDataSet
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider1'
    Left = 512
    Top = 72
    object ClientDataSet1TT_FORMSETTINGS_ID: TIntegerField
      FieldName = 'TT_FORMSETTINGS_ID'
      Required = True
    end
    object ClientDataSet1TT_EMP_ID: TIntegerField
      FieldName = 'TT_EMP_ID'
    end
    object ClientDataSet1TT_FORM: TStringField
      FieldName = 'TT_FORM'
      Size = 50
    end
    object ClientDataSet1TT_VERSION: TIntegerField
      FieldName = 'TT_VERSION'
    end
    object ClientDataSet1TT_VIEWDATA: TMemoField
      FieldName = 'TT_VIEWDATA'
      BlobType = ftWideMemo
    end
  end
  object FDConnection2: TFDConnection
    Params.Strings = (
      'Database=test'
      'Password=test'
      'User_Name=test'
      'Server=VS2003-2008'
      'DriverID=MSSQL')
    FormatOptions.AssignedValues = [fvMapRules]
    FormatOptions.OwnMapRules = True
    FormatOptions.MapRules = <
      item
        SourceDataType = dtDateTimeStamp
        TargetDataType = dtDateTime
      end>
    Connected = True
    LoginPrompt = False
    Left = 312
    Top = 144
  end
  object FDQuery2: TFDQuery
    Connection = FDConnection2
    SQL.Strings = (
      'select * from tt_formsettings')
    Left = 416
    Top = 144
  end
  object DataSetProvider2: TDataSetProvider
    DataSet = FDQuery2
    Left = 464
    Top = 144
  end
  object ClientDataSet2: TClientDataSet
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider2'
    Left = 512
    Top = 144
    object ClientDataSet2TT_FORMSETTINGS_ID: TIntegerField
      FieldName = 'TT_FORMSETTINGS_ID'
      Required = True
    end
    object ClientDataSet2TT_EMP_ID: TIntegerField
      FieldName = 'TT_EMP_ID'
    end
    object ClientDataSet2TT_FORM: TStringField
      FieldName = 'TT_FORM'
      Size = 50
    end
    object ClientDataSet2TT_VERSION: TIntegerField
      FieldName = 'TT_VERSION'
    end
    object ClientDataSet2TT_VIEWDATA: TMemoField
      FieldName = 'TT_VIEWDATA'
      BlobType = ftMemo
    end
  end
  object FDConnection0: TFDConnection
    Params.Strings = (
      'Database=D:\Testing\Diverse\FireDacOracleBlob\TIMETELL_DEMO.GDB'
      'User_Name=SYSDBA'
      'Password=masterkey'
      'DriverID=IB')
    FormatOptions.AssignedValues = [fvMapRules]
    FormatOptions.OwnMapRules = True
    FormatOptions.MapRules = <
      item
        SourceDataType = dtDateTimeStamp
        TargetDataType = dtDateTime
      end
      item
        SourceDataType = dtSingle
        TargetDataType = dtDouble
      end>
    Connected = True
    LoginPrompt = False
    Left = 312
    Top = 8
  end
  object FDQuery0: TFDQuery
    Connection = FDConnection0
    SQL.Strings = (
      'select * from tt_formsettings')
    Left = 416
    Top = 8
  end
  object DataSetProvider0: TDataSetProvider
    DataSet = FDQuery0
    Left = 464
    Top = 8
  end
  object ClientDataSet0: TClientDataSet
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider0'
    Left = 512
    Top = 8
    object ClientDataSet0TT_FORMSETTINGS_ID: TIntegerField
      FieldName = 'TT_FORMSETTINGS_ID'
      Required = True
    end
    object ClientDataSet0TT_EMP_ID: TIntegerField
      FieldName = 'TT_EMP_ID'
    end
    object ClientDataSet0TT_FORM: TStringField
      FieldName = 'TT_FORM'
      Size = 50
    end
    object ClientDataSet0TT_VERSION: TIntegerField
      FieldName = 'TT_VERSION'
    end
    object ClientDataSet0TT_VIEWDATA: TMemoField
      FieldName = 'TT_VIEWDATA'
      BlobType = ftMemo
    end
  end
  object FDPhysMSSQLDriverLink1: TFDPhysMSSQLDriverLink
    Left = 368
    Top = 144
  end
  object FDPhysFBDriverLink1: TFDPhysFBDriverLink
    Left = 368
    Top = 8
  end
end

Примечание. Тот факт, что назначение параметров теперь (также) работает, находится в Data Сопоставление типов (FireDAC) документация:

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

person Jan Doggen    schedule 11.12.2017