Parameter.AsString 在 Oracle/MSSQL 下失败 - Parameter.Value Oracle 下的 2 字节字符
Parameter.AsString failing under Oracle/MSSQL - Parameter.Value 2-byte chars under 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 字符串类型...
但是现在 S 分配对于 Oracle(不是 FireBird 或 MSSQL)失败了,因为我看到返回了 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'...
我可以处理
S := TEncoding.Unicode.GetString(FieldByName('TT_VIEWDATA').AsBytes);
对于 Oracle,但是(当然)在使用其他两种不起作用的数据库类型时:
No mapping for the Unicode character exists in the target multi-byte code page
我在这里错过了什么?具体来说,我只想让 AsString retrievals/assignments 工作。
请注意 设置 AsString 属性 将 DataType 属性 设置为 ftWideString 或 ftString FireDAC TFDParam.AsString documentation 中的注释。似乎参数值分配只是将类型从 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%'
returns NLS_CHARACTERSET=WE8MSWIN1252
和 NLS_NCHAR_CHARACTERSET=AL16UTF16
查询 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;
在一个小型测试应用程序(直接连接到 Oracle;不是客户端-服务器)中,我让 Delphi 添加字段定义,然后它说:
DataFormsettingsTT_VIEWDATA: TWideMemoField;
如果我在主应用程序中使用它,Oracle 工作正常,但随后我得到 'garbage' for MSSQL。
我还尝试为 Oracle 连接设置映射规则,例如(许多变体):
with AConnection.FormatOptions.MapRules.Add do
begin
SourceDataType := dtWideMemo;
TargetDataType := dtMemo;
end;
AConnection.FormatOptions.OwnMapRules := true;
但这并没有帮助。
这不是最终的解决方案,请参阅代码块之前的最后评论。它仍然感觉像一个黑客。我没有将它添加到问题中(如 'attempts'),因为最终这会起作用。
发生了两件事,它们都可以通过以下更改解决:
- Param 类型已更改 Params 值分配错误
- 字段定义和
FieldByName().AsString
retrieval/assigning 不工作
请注意,我在整个应用程序中受到设计时字段定义的限制,该应用程序必须处理所有三种数据库类型,特别是 DataFormSettingsTT_VIEWDATA
持久字段是 TMemoField
。
使用问题底部提到的 table 定义,如果您设置 TFDConnection -> TFDQuery -> TDataSetProvider -> TClientDataSet
并使用 添加所有字段[=79= 添加字段定义], DataFormSettingsTT_VIEWDATA
将是类型:
TMemoField
与 BlobType=ftMemoField
for FireBird
TMemoField
和 BlobType=ftWideMemoField
for MSSQL
TWideMemoField
对于 Oracle BlobType=ftWideMemoField
。
手动编辑 .DFM 和 .PAS 以将 Oracle TWideMemoField
设置回 TMemoField
可行(好吧,我不必更改它,它是遗留代码)如果我还:
force BlobType=ftWideMemoField
for the design time TMemoField
s at 运行 time(我可以在父级的 OnCreate 中做到这一点,我的所有数据模块都来自);
处理字符串检索 仅适用于 Oracle 作为 TEncoding.Unicode.GetString(FieldByName(SFormSettingsViewData).AsBytes)
。
但这仍然不是最优的。我的客户端代码带有 TClientDataSet will now have to know what kind of database it is。我可以在客户端应用程序中查询服务器。
这是一个包含这些更改的示例应用程序:
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 Type Mapping (FireDAC) 文档中:
In case of a result set column, each rule defines a transformation of a source data type, returned by a driver, into a target one, preferred by an application. In case of a command parameter, the rule defines a transformation of a target data type, specified by an application, into a source data type, supported by a driver. All rules, excluding the name-based ones, work bidirectionally for both cases.
这是它不起作用的原因:
在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
工作正常。
在 Embarcadero 质量门户中报告为 RSP-19600。
为了完整起见:因为我的另一个答案中提到的映射不再有效,您必须使用 .Value
而不是 .AsString.
来更改对参数的访问
更改为 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 字符串类型...
但是现在 S 分配对于 Oracle(不是 FireBird 或 MSSQL)失败了,因为我看到返回了 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'...
我可以处理
S := TEncoding.Unicode.GetString(FieldByName('TT_VIEWDATA').AsBytes);
对于 Oracle,但是(当然)在使用其他两种不起作用的数据库类型时:
No mapping for the Unicode character exists in the target multi-byte code page
我在这里错过了什么?具体来说,我只想让 AsString retrievals/assignments 工作。
请注意 设置 AsString 属性 将 DataType 属性 设置为 ftWideString 或 ftString FireDAC TFDParam.AsString documentation 中的注释。似乎参数值分配只是将类型从 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%'
returnsNLS_CHARACTERSET=WE8MSWIN1252
和NLS_NCHAR_CHARACTERSET=AL16UTF16
查询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;
在一个小型测试应用程序(直接连接到 Oracle;不是客户端-服务器)中,我让 Delphi 添加字段定义,然后它说:
DataFormsettingsTT_VIEWDATA: TWideMemoField;
如果我在主应用程序中使用它,Oracle 工作正常,但随后我得到 'garbage' for MSSQL。
我还尝试为 Oracle 连接设置映射规则,例如(许多变体):
with AConnection.FormatOptions.MapRules.Add do
begin
SourceDataType := dtWideMemo;
TargetDataType := dtMemo;
end;
AConnection.FormatOptions.OwnMapRules := true;
但这并没有帮助。
这不是最终的解决方案,请参阅代码块之前的最后评论。它仍然感觉像一个黑客。我没有将它添加到问题中(如 'attempts'),因为最终这会起作用。
发生了两件事,它们都可以通过以下更改解决:
- Param 类型已更改 Params 值分配错误
- 字段定义和
FieldByName().AsString
retrieval/assigning 不工作
请注意,我在整个应用程序中受到设计时字段定义的限制,该应用程序必须处理所有三种数据库类型,特别是 DataFormSettingsTT_VIEWDATA
持久字段是 TMemoField
。
使用问题底部提到的 table 定义,如果您设置 TFDConnection -> TFDQuery -> TDataSetProvider -> TClientDataSet
并使用 添加所有字段[=79= 添加字段定义], DataFormSettingsTT_VIEWDATA
将是类型:
TMemoField
与BlobType=ftMemoField
for FireBirdTMemoField
和BlobType=ftWideMemoField
for MSSQLTWideMemoField
对于 OracleBlobType=ftWideMemoField
。
手动编辑 .DFM 和 .PAS 以将 Oracle TWideMemoField
设置回 TMemoField
可行(好吧,我不必更改它,它是遗留代码)如果我还:
force
BlobType=ftWideMemoField
for the design timeTMemoField
s at 运行 time(我可以在父级的 OnCreate 中做到这一点,我的所有数据模块都来自);处理字符串检索 仅适用于 Oracle 作为
TEncoding.Unicode.GetString(FieldByName(SFormSettingsViewData).AsBytes)
。
但这仍然不是最优的。我的客户端代码带有 TClientDataSet will now have to know what kind of database it is。我可以在客户端应用程序中查询服务器。
这是一个包含这些更改的示例应用程序:
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 Type Mapping (FireDAC) 文档中:
In case of a result set column, each rule defines a transformation of a source data type, returned by a driver, into a target one, preferred by an application. In case of a command parameter, the rule defines a transformation of a target data type, specified by an application, into a source data type, supported by a driver. All rules, excluding the name-based ones, work bidirectionally for both cases.
这是它不起作用的原因:
在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
工作正常。
在 Embarcadero 质量门户中报告为 RSP-19600。
为了完整起见:因为我的另一个答案中提到的映射不再有效,您必须使用 .Value
而不是 .AsString.