Delphi/FireDACStrsTrim2Len 对 ParamByName 没有影响
Delphi/FireDAC StrsTrim2Len has no impact on ParamByName
使用 Delphi XE7 和 Tokyo with Firebird 2.5 我得出的结论是 StrsTrim2Len
在使用 TFDQuery
和 [= 进行 updates/inserts 时没有任何影响16=],这使得过大的字符串引发异常。
除了截断代码中的所有字符串之外,还有其他方法吗:
ParamByName('Field1').AsString := SomeVar.SubString(0, 50);
添加后还需要跟踪字段长度吗?
来源和形式是:
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
System.StrUtils,
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.FB,
FireDAC.Phys.FBDef, FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt,
Vcl.StdCtrls, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client, FireDAC.Comp.UI, FireDAC.Phys.IBBase;
type
TForm3 = class(TForm)
FDConnection1: TFDConnection;
FDPhysFBDriverLink1: TFDPhysFBDriverLink;
FDGUIxWaitCursor1: TFDGUIxWaitCursor;
FDQuery1: TFDQuery;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
begin
FDConnection1.Open;
FDQuery1.FormatOptions.StrsTrim2Len := True;
FDQuery1.SQL.Text := 'INSERT INTO MyTable (ID, MyField) VALUES (:ID, :MyField)';
FDQuery1.ParamByName('ID').AsInteger := 1;
FDQuery1.ParamByName('MyField').AsString := DupeString('0', 21); { ← field is 20 chars }
FDQuery1.ExecSQL;
FDQuery1.SQL.Text := 'SELECT MyField FROM MyTable WHERE ID = 1';
FDQuery1.Open;
Assert(Length(FDQuery1.FieldByName('MyField').AsString) = 20); { ← trimmed to 20 chars? }
FDConnection1.Close;
end;
end.
对应的.dfm文件:
object Form3: TForm3
Left = 0
Top = 0
Caption = 'Form3'
ClientHeight = 294
ClientWidth = 161
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 40
Top = 16
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object FDConnection1: TFDConnection
Params.Strings = (
'Database=MyUTF8Db'
'User_Name=Sysdba'
'Password='
'Server=127.0.0.1'
'CharacterSet=UTF8'
'DriverID=FB')
FormatOptions.AssignedValues = [fvStrsTrim2Len]
FormatOptions.StrsTrim2Len = True
Left = 48
Top = 48
end
object FDPhysFBDriverLink1: TFDPhysFBDriverLink
Left = 48
Top = 104
end
object FDGUIxWaitCursor1: TFDGUIxWaitCursor
Provider = 'Forms'
ScreenCursor = gcrHourGlass
Left = 48
Top = 160
end
object FDQuery1: TFDQuery
Connection = FDConnection1
Left = 48
Top = 216
end
end
是否需要在启用 StrsTrim2Len 的情况下手动 trim 参数值?
不,当您启用 StrsTrim2Len 选项时,即使分配的参数值也应 trim 达到绑定字段的限制。因此,对于字符串字段参数,直接字符串赋值应该 trim 的值 in length,恕我直言:
ParamByName('MyFieldUTF8').AsWideString := 'Value to be encoded by FireDAC';
为什么恕我直言?因为在这种特殊情况下(Delphi Tokyo,Firebird 中的 UTF-8 字段),此选项的实现类似于 DataTrim2Size 而不是它的读取方式,我不确定是否它被遗忘是有原因的。 Firebird 中的字符串字段在定义时受字符串长度限制而不是存储数据大小限制的限制(据我所知)。
自动字符串参数值有什么问题trimming then?
FireDAC,现在为 Firebird 驱动程序实现,trims 参数数据缓冲区仅当 数据大小[=编码参数值的47=]超过参数字段的存储数据大小(启用StrsTrim2Len选项时)。所以它是基于数据 size 而不是字符串 length.
参数数据大小(如果未指定)似乎是从 RDB$FIELD_LENGTH RDB$FIELDS[=47 的元字段中获得的=] 系统 table (无法确认,但似乎 sqllen 成员由该字段值填充,来自快速的 Firebird 代码浏览)。不过暂时没那么重要。
问题是这段代码和 StrsTrim2Len 选项实际上应该在这里做什么的类比(它是 TIBVariable.SetData 方法实现,UTF-8 分支; 我添加的评论):
{ this encodes the value to UTF-8 and returns number of bytes written to the buffer }
iByteLen := FVars.Statement.Database.Encoder.Encode(ApData, ALen, pUTF8, ecUTF16);
{ DataSize here equals to the RDB$FIELD_LENGTH, so let's try a calculation for field
let's say VARCHAR(20), and to the parameter binded to it assign e.g. 21 chars long
string consisting from ANSI chars:
iByteLen → 21 ANSI chars occupies 21 bytes
DataSize → XSQLVAR.sqllen → RDB$FIELD_LENGTH → 80 (20 chars * max UTF-8 char size?)
Now, is 21 > 80? No? No trimming then. }
if iByteLen > DataSize then
if FVars.Statement.StrsTrim2Len then
iByteLen := DataSize
else
ErrorDataTooLarge(DataSize, iByteLen);
你可能猜到了,参数值缓冲区的 trimming 实际上会发生,但是编码后的字符串太小了,所以 trimming 被跳过了。你也可以预测,如果你使用这样的参数,例如对于插入值,您最终会遇到引擎异常,因为引擎在验证约束时使用插入的字符串值长度而不是数据大小。
我在这里找不到处理数据大小的实际意义。好吧,你永远不会溢出存储数据大小,但另一方面你可以很容易地超过字段的长度。并且讨论的选项是关于长度,而不是关于大小。
我将打开一个错误报告并尝试等待有关此主题的一些评论,因为我不认为这段代码是意外。会回来报告..
使用 Delphi XE7 和 Tokyo with Firebird 2.5 我得出的结论是 StrsTrim2Len
在使用 TFDQuery
和 [= 进行 updates/inserts 时没有任何影响16=],这使得过大的字符串引发异常。
除了截断代码中的所有字符串之外,还有其他方法吗:
ParamByName('Field1').AsString := SomeVar.SubString(0, 50);
添加后还需要跟踪字段长度吗?
来源和形式是:
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
System.StrUtils,
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.FB,
FireDAC.Phys.FBDef, FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt,
Vcl.StdCtrls, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client, FireDAC.Comp.UI, FireDAC.Phys.IBBase;
type
TForm3 = class(TForm)
FDConnection1: TFDConnection;
FDPhysFBDriverLink1: TFDPhysFBDriverLink;
FDGUIxWaitCursor1: TFDGUIxWaitCursor;
FDQuery1: TFDQuery;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
begin
FDConnection1.Open;
FDQuery1.FormatOptions.StrsTrim2Len := True;
FDQuery1.SQL.Text := 'INSERT INTO MyTable (ID, MyField) VALUES (:ID, :MyField)';
FDQuery1.ParamByName('ID').AsInteger := 1;
FDQuery1.ParamByName('MyField').AsString := DupeString('0', 21); { ← field is 20 chars }
FDQuery1.ExecSQL;
FDQuery1.SQL.Text := 'SELECT MyField FROM MyTable WHERE ID = 1';
FDQuery1.Open;
Assert(Length(FDQuery1.FieldByName('MyField').AsString) = 20); { ← trimmed to 20 chars? }
FDConnection1.Close;
end;
end.
对应的.dfm文件:
object Form3: TForm3
Left = 0
Top = 0
Caption = 'Form3'
ClientHeight = 294
ClientWidth = 161
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 40
Top = 16
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object FDConnection1: TFDConnection
Params.Strings = (
'Database=MyUTF8Db'
'User_Name=Sysdba'
'Password='
'Server=127.0.0.1'
'CharacterSet=UTF8'
'DriverID=FB')
FormatOptions.AssignedValues = [fvStrsTrim2Len]
FormatOptions.StrsTrim2Len = True
Left = 48
Top = 48
end
object FDPhysFBDriverLink1: TFDPhysFBDriverLink
Left = 48
Top = 104
end
object FDGUIxWaitCursor1: TFDGUIxWaitCursor
Provider = 'Forms'
ScreenCursor = gcrHourGlass
Left = 48
Top = 160
end
object FDQuery1: TFDQuery
Connection = FDConnection1
Left = 48
Top = 216
end
end
是否需要在启用 StrsTrim2Len 的情况下手动 trim 参数值?
不,当您启用 StrsTrim2Len 选项时,即使分配的参数值也应 trim 达到绑定字段的限制。因此,对于字符串字段参数,直接字符串赋值应该 trim 的值 in length,恕我直言:
ParamByName('MyFieldUTF8').AsWideString := 'Value to be encoded by FireDAC';
为什么恕我直言?因为在这种特殊情况下(Delphi Tokyo,Firebird 中的 UTF-8 字段),此选项的实现类似于 DataTrim2Size 而不是它的读取方式,我不确定是否它被遗忘是有原因的。 Firebird 中的字符串字段在定义时受字符串长度限制而不是存储数据大小限制的限制(据我所知)。
自动字符串参数值有什么问题trimming then?
FireDAC,现在为 Firebird 驱动程序实现,trims 参数数据缓冲区仅当 数据大小[=编码参数值的47=]超过参数字段的存储数据大小(启用StrsTrim2Len选项时)。所以它是基于数据 size 而不是字符串 length.
参数数据大小(如果未指定)似乎是从 RDB$FIELD_LENGTH RDB$FIELDS[=47 的元字段中获得的=] 系统 table (无法确认,但似乎 sqllen 成员由该字段值填充,来自快速的 Firebird 代码浏览)。不过暂时没那么重要。
问题是这段代码和 StrsTrim2Len 选项实际上应该在这里做什么的类比(它是 TIBVariable.SetData 方法实现,UTF-8 分支; 我添加的评论):
{ this encodes the value to UTF-8 and returns number of bytes written to the buffer }
iByteLen := FVars.Statement.Database.Encoder.Encode(ApData, ALen, pUTF8, ecUTF16);
{ DataSize here equals to the RDB$FIELD_LENGTH, so let's try a calculation for field
let's say VARCHAR(20), and to the parameter binded to it assign e.g. 21 chars long
string consisting from ANSI chars:
iByteLen → 21 ANSI chars occupies 21 bytes
DataSize → XSQLVAR.sqllen → RDB$FIELD_LENGTH → 80 (20 chars * max UTF-8 char size?)
Now, is 21 > 80? No? No trimming then. }
if iByteLen > DataSize then
if FVars.Statement.StrsTrim2Len then
iByteLen := DataSize
else
ErrorDataTooLarge(DataSize, iByteLen);
你可能猜到了,参数值缓冲区的 trimming 实际上会发生,但是编码后的字符串太小了,所以 trimming 被跳过了。你也可以预测,如果你使用这样的参数,例如对于插入值,您最终会遇到引擎异常,因为引擎在验证约束时使用插入的字符串值长度而不是数据大小。
我在这里找不到处理数据大小的实际意义。好吧,你永远不会溢出存储数据大小,但另一方面你可以很容易地超过字段的长度。并且讨论的选项是关于长度,而不是关于大小。
我将打开一个错误报告并尝试等待有关此主题的一些评论,因为我不认为这段代码是意外。会回来报告..