使用Delphi XE7和Tokyo与Firebird 2.5我得出的结论是StrsTrim2Len
在使用TFDQuery
和ParamByName
进行更新/插入时没有任何影响,这会产生超大的字符串提出异常。
除了截断代码中的所有字符串之外还有其他方法,如:
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
答案 0 :(得分:0)
不,当您启用StrsTrim2Len选项时,即使已分配的参数值也应修剪为绑定字段的限制。因此,对于字符串字段参数,直接字符串赋值应修剪长度值,恕我直言:
ParamByName('MyFieldUTF8').AsWideString := 'Value to be encoded by FireDAC';
为什么恕我直言?因为在这个特殊情况下(Delphi东京,Firebird中的UTF-8字段),此选项的实现类似于 DataTrim2Size 而不是它的读取方式,我不确定是否有某些原因让它离开背后。 Firebird中的字符串字段受字符串长度限制的约束,而不是定义时的存储数据大小限制(据我所知)。
FireDAC,现在为Firebird驱动程序实现,只有当编码参数值的数据大小超过存储时,修剪参数数据缓冲区才能获得UTF-8字段参数字段的数据大小(启用StrsTrim2Len选项时)。因此,它基于数据大小而不是字符串长度。
参数数据大小(如果未指定)似乎是从 RDB $ FIELDS 系统表的 RDB $ FIELD_LENGTH 元字段获得的(无法确认,但似乎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);
正如您可能猜到的那样,实际上会对参数值缓冲区进行修剪,但编码后的字符串太小,因此跳过了修剪。你也可以预测,如果你使用这样的参数,例如为了插入一个值,你最终会得到引擎异常,因为在验证约束时引擎使用插入的字符串值长度而不是数据大小。
我在这里找不到使用数据大小的实际意义。好吧,您永远不会溢出存储数据大小,但另一方面,您可以轻松超过该字段的长度。讨论的选项是长度,而不是大小。
我会打开错误报告并尝试等待有关此主题的评论,因为我不相信此代码是意外。会报告回来..