在Oracle/MSSQL下的Parameter.AsString失败 - 在Oracle下的Parameter.Value 2字节字符

Jan*_*gen 8 delphi oracle firedac delphi-10.2-tokyo

更改为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;   
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

连接到MSSQL或Oracle数据库时; 连接到FireBird时没有.
之后FetchParams,DataFormsettings.params[1].datatype永远是一个ftString.

如果我更换

Params.ParamByName('TT_FORM').AString := UpperCase(AKey);  
Run Code Online (Sandbox Code Playgroud)

Params.ParamByName('TT_FORM').Value := UpperCase(AKey);
Run Code Online (Sandbox Code Playgroud)

... Open语句中没有错误.虽然我没有真正理解错误,但我认为这已经解决了.毕竟,这应该是所有默认的Delphi字符串类型......
但是现在,对于Oracle(而不是FireBird或MSSQL)来说,S分配失败,因为我看到返回2字节字符.S包含:

\'#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'...
Run Code Online (Sandbox Code Playgroud)

我可以用例如

S := TEncoding.Unicode.GetString(FieldByName('TT_VIEWDATA').AsBytes);  
Run Code Online (Sandbox Code Playgroud)

对于Oracle,但(当然)在使用其他两种不起作用的数据库类型时:

No mapping for the Unicode character exists in the target multi-byte code page
Run Code Online (Sandbox Code Playgroud)

我在这里错过了什么?具体来说,我想让AsString检索/分配工作.
请注意,设置AsString属性会FireDAC TFDParam.AsString文档中将DataType属性设置为ftWideString或ftString备注.看起来好像参数值赋值只是将类型从ftString切换到ftWideString(由原始错误指示).

DataFormSettings是一个TClientDataSet在客户端应用程序,连接到其中一个服务器应用TDataSetProviderTFDQuery驻留.查询是

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
Run Code Online (Sandbox Code Playgroud)

表格创建如下:

火鸟:

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
);
Run Code Online (Sandbox Code Playgroud)

甲骨文:

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
);
Run Code Online (Sandbox Code Playgroud)

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
);
Run Code Online (Sandbox Code Playgroud)

我检查过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...
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 目前正在测试SQL Server 2008和Oracle 10,但我希望这对其他版本没有什么不同.
  • FWIW,select * from NLS_database_PARAMETERS where parameter like '%CHARACTERSET%'returns NLS_CHARACTERSET=WE8MSWIN1252NLS_NCHAR_CHARACTERSET=AL16UTF16
    Query SELECT dump(dbms_lob.substr(tt_viewdata,100,1), 1016), tt_viewdata FROM tt_formsettings确认CLOB包含Win1252代码页的ASCII字节:
    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;
Run Code Online (Sandbox Code Playgroud)

在一个小的testapp(直接连接到Oracle;而不是客户端 - 服务器)我让Delphi添加字段定义然后它说:

DataFormsettingsTT_VIEWDATA: TWideMemoField;
Run Code Online (Sandbox Code Playgroud)

如果我在主应用程序中使用它,Oracle工作正常,但后来我得到MSSQL的"垃圾".

我还尝试为Oracle连接设置映射规则,如(许多变体):

with AConnection.FormatOptions.MapRules.Add do
begin
  SourceDataType := dtWideMemo;
  TargetDataType := dtMemo;
end;
AConnection.FormatOptions.OwnMapRules := true;
Run Code Online (Sandbox Code Playgroud)

但这没有帮助.

Jan*_*gen 5

这是它不起作用的原因:

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;
Run Code Online (Sandbox Code Playgroud)

的确,可能是问题.

解决方案是添加转换dtWideHMemo为的映射规则dtMemo.
之后,读取和写入CLOB .AsString工作正常.

在Embarcadero Quality Portal中报告为RSP-19600.


为了完整性:因为我的其他答案中提到的映射不再处于活动状态,您必须更改对参数的访问.Value而不是.AsString.