[All]
FireDACでFireBirdへ接続時、入力する文字数によってエラーが発生する症状について
Abstract: 発生している症状に関する原因と解決方法について説明します。

対象となるIDE製品
・RAD Studio/C++Builder/Delphi XE5

対象となるデータベースコンポーネント
・FireDAC (9.0.1)

対象となるデータベース
・FireBird 1.5以降

問題

例えば、FireBirdのデータベースのキャラクタセットを”UTF8”で作成し、
以下のようなテーブルを作成します。
create table Table1(
DATA varchar(20)
);

XE4までは、FireDACでFireBirdに接続し、 varchar(20)のカラムに日本語文字を20文字登録した場合、エラーは発生しませんでしたが、
XE5では、同様にFireDACでFireBirdに接続し、 varchar(20)のカラムに日本語を20文字登録した場合、以下のようなエラーが発生します。


図1: エラー画面

原因

本件が発生する原因は以下の2点が理由です。

(1)DriverIDの指定が異なる
XE4までのFireDACは、InterBase/FireBirdどちらもDriverID=IB でしたが、 XE5からはFireBird専用のDriverIDが利用でき、DriverID=FBと指定することができるようなりました。

(2)TWideStringField型のサイズがDriverIDによって異なる
データベースのキャラクタセットが”UTF8”の場合、 FireBird/InterBaseのvarcharのカラムは、TWideStringField型にマッピングされます。
XE4までは、InterBaseとFireBirdはDriverIDが共用だったため、例えば、varchar(20)のカラムは、どちらのデータベースもTWideStringField型、Size=80としてマッピングされました。

しかしながら、XE5からはDriverIDがFireBirdとInterBaseで明確に分かれたため、 DriverIDの違いにより、TWideStringField型へマッピングされる際のサイズの仕様が変更になりました。
InterBaseは従来通り、varchar(20)の場合、TWideStringFieldのSize=60ですが、 FireBirdは、varchar(20)の場合、TWideStringFieldのSize=20になります。
上記の結果、DriverIDによって、マッピングされるサイズの仕様が変更されましたが、日本語文字を入力し、その文字数は、本来変更されたFireBirdの文字数で判定されるべきですが、
従来のInterBaseの文字数のままで判定している箇所があり、それによって不具合が生じました。

解決
本件の回避策としてFireDAC.Phys.IBBase.pasファイルをご自身のプロジェクトへコピーし、以下のように修正してください
FireDAC.Phys.IBBase.pasファイルは、以下のパスに配置されています。
Windows 32ビットOSの場合
C:\Program Files\Embarcadero\RAD Studio\12.0\source\data\firedac
Windows 64ビットOSの場合
C:\Program Files (x86)\Embarcadero\RAD Studio\12.0\source\data\firedac
この修正はXE5 Update2をベースとしていますので、必ず、XE5 Update2へアップデートしてください。
2291行
=============
(修正前)
iPrec, iScale: Integer;
(修正後)
iPrec, iScale, iLen: Integer;
2413行
===============
(修正前)
SetupStringVariable(oParam, FParInfos[iBase + i], oVar)
else if pInfo^.FADLen <> 0 then
oVar.sqllen := pInfo^.FADLen;
(修正後)
SetupStringVariable(oParam, FParInfos[iBase + i], oVar)
else if pInfo^.FADLen <> 0 then
if (IBConnection.FEnv.Lib.Brand = ibFirebird) and
((pInfo^.FDestSQLDataType = SQL_VARYING) or
(pInfo^.FDestSQLDataType = SQL_TEXT)) then begin
iLen := pInfo^.FADLen * LongWord(IBConnection.FEnv.Lib.GetBytesPerChar(oVar.sqlsubtype));
if iLen > 32765 then
iLen := 32765;
oVar.sqllen := iLen;
end
else
oVar.sqllen := pInfo^.FADLen;