使用Delphi XE7和东京与Firebird2.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
发布于 2017-09-26 07:14:09
是否需要在启用StrsTrim2Len的情况下手动修剪参数值?
否,启用StrsTrim2Len选项时,即使已分配的参数值也应修剪到绑定字段的限制。因此,直接字符串赋值应该为字符串字段参数IMHO修剪长度为的值:
ParamByName('MyFieldUTF8').AsWideString := 'Value to be encoded by FireDAC';
为什么是IMHO?因为在这个特殊情况下(Delphi东京,Firebird中的UTF-8字段),这个选项的实现类似于DataTrim2Size,而不是它的读取方式,我不确定它是否有什么理由被抛在后面。Firebird中的字符串字段受字符串长度限制的约束,而不是在定义存储数据大小限制时(据我所知)。
那么,自动字符串参数值修整有什么问题呢?
FireDAC是为火鸟驱动程序实现的,只有当编码参数值的数据大小超过参数字段的存储数据大小时(当启用StrsTrim2Len选项时),才会为UTF-8字段设置参数数据缓冲区。因此,它是基于数据大小而不是字符串长度的。
参数数据大小(如果没有指定)似乎是从RDB$FIELD_LENGTH RDB$系统表的元字段中获得的(不能确认这一点,但似乎sqllen成员是由该字段值填充的,通过快速浏览防火墙代码)。但现在没那么重要了。
问题在于这段代码以及StrsTrim2Len选项在这里实际应该做什么(这是StrsTrim2Len方法实现,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);
正如您可能猜到的,参数值缓冲区的修整实际上是会发生的,但是编码的字符串太小,所以会跳过修整。您还可以预测,如果您使用这样的参数(例如,用于插入一个值),您将得到引擎异常,因为引擎在验证约束时使用插入的字符串值长度而不是数据大小。
我在这里找不到处理数据大小的实际意义。好的,您永远不会溢出存储数据大小,但是另一方面,您可以很容易地超过字段的长度。讨论的选项是关于长度,而不是大小。
我将打开一个bug报告,并尝试等待围绕这个主题的一些评论,因为我不相信这段代码是一个意外。会汇报..。
https://stackoverflow.com/questions/46283578
复制相似问题