首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Delphi/FireDAC StrsTrim2Len对ParamByName没有影响

Delphi/FireDAC StrsTrim2Len对ParamByName没有影响
EN

Stack Overflow用户
提问于 2017-09-18 15:55:48
回答 1查看 648关注 0票数 1

使用Delphi XE7和东京与Firebird2.5,我得出的结论是,StrsTrim2Len在使用TFDQueryParamByName进行更新/插入时没有任何影响,这会导致超大型字符串引发异常。

除了截断代码中的所有字符串之外,还有其他方法,例如:

代码语言:javascript
运行
复制
ParamByName('Field1').AsString := SomeVar.SubString(0, 50);

增加了跟踪字段长度的需要吗?

其来源和形式如下:

代码语言:javascript
运行
复制
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文件:

代码语言:javascript
运行
复制
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
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-09-26 07:14:09

是否需要在启用StrsTrim2Len的情况下手动修剪参数值?

否,启用StrsTrim2Len选项时,即使已分配的参数值也应修剪到绑定字段的限制。因此,直接字符串赋值应该为字符串字段参数IMHO修剪长度为的值

代码语言:javascript
运行
复制
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分支;我添加的注释):

代码语言:javascript
运行
复制
{ 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报告,并尝试等待围绕这个主题的一些评论,因为我不相信这段代码是一个意外。会汇报..。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46283578

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档