フォーラム


ゲスト  

ようこそ ゲスト さん。このフォーラムに投稿するには 登録が必要です。

ページ: [1]
トピック: IBConsoleの動作について
ROMG
メンバー
投稿数: 2
IBConsoleの動作について
on: 2015/10/23 23:25 Fri

はじめまして。ROM ONLYの年寄です。
どこにカキコめばわからないので、ここに書かせていただきます。

当方、趣味でDB作成、Delphi Programmingを楽しんでおります。
Delphi2007を使ってソフトを作成中ですが今回はDelphiとは関係ありません。
(今のところ最新版DelphiへのUPDATEはいつのことになるやら…です)
DEKOさんのIBConsoleにはansi版の初期の頃からありがたく使わせていただいています。

環境は
OS:Windows10
DB:Firebird2.5(64bit)Diarect 3, character set UTF8
IBConsole:ibc_unicode_rel29_win64(発見時)rel50_win64(現在)です。

今までは、文字列にはVarchar/BLOBを使ってきたのですが、今回初めてField定義に
複数のCharフィールドを使いました。
(それぞれ最大文字数および大多数のデータが最大文字数であることがわかっていたので)

IBConsoleのオブジェクトインスペクタでテストデータを確認中、下記の如きおかしな現象に出くわしました。

文字列に、Char(n) character set utf8フィールドを定義したテーブルの場合、
IBConsoleのオブジェクトインスペクタのデータタブ画面(データグリッド)で
・Charフィールドのデータが漢字、かな等のマルチバイト文字の場合データ文字列の後ろにゴミ文字列が現れます。
 (他の数値フィールド、Varcharフィールドの表示には問題なし)

・ゴミ付きのままだとCharフィールド以外のフィールドも編集後の他行への移動、COMMITが不可です。
 ”長さ12の文字列を容量8のフィールドに格納しようとしています。”のメッセージが表示される。
 (メッセージ内の数値は例で、データ長>定義フィールド長とメッセージされます)
 
・INTフィールドを編集する場合でも、同時にCharフィールド(複数フィールドあれば全て)のゴミを除く処理をしないと
 他行への移動が不可です。移動後COMMITすれば、またゴミが表示されます。

・データグリッドの空白行へのデータ書き込みは可能ですが、COMMITして再表示すると同様にゴミが付きます。

・UTF8で定義していてもすべての全CharフィールドのデータがASCII文字の場合は表示上、ゴミは現れませんし、
 Char以外のフィールドの編集、移動、COMMITが可能です。

オブジェクトインスペクタのプロパティ画面からChar→Varcharに変更するとデータタブ画面での
表示のゴミが消え、データ編集、COMMITが問題なく実行できるようになります。

インタラクティブSQLを使ってのINSERT/UPDATE文は問題なく実行できますが、
SELECT文を投げて表示されるデータグリッドにも同様にCharフィールドではデータの後ろにゴミが付きます。

私の実力では、データグリッドは
編集時の文字列エリアのハイライト長からChar定義文字数の4倍のByte長を確保している。
Charフィールドへの入力時は、全角・半角の混在時でも文字数をきちんとカウントしている。
ぐらいしかわかりません。

なお、
FirebirdのISQLを使ったSELECTではゴミはついていませんでした
ansi版のIBCOSLEはまだ試していません。

一度、確認していただけないでしょうか。

DEKO
管理者
投稿数: 2690
Re: IBConsoleの動作について
on: 2015/10/24 21:13 Sat

いらっしゃいまし。

IBConsole と…というより IBX とCHAR() フィールドの件ですが、これは文字コードが何であっても発生します。SJIS_0208 でも UNICODE_FSS でも同様です。指定した文字数に満たない場合には空白文字でパディングしてくれちゃいます (固定長なので仕様的にはこれで正しいっちゃー正しいですけれど)。

Delphi でこの件を修正するには、アクティブにした TIBTable / TIBQuery の文字列フィールドにある FixedChar プロパティを False に設定します。

  for i:=0 to IBTable.Fields.Count-1 do
case IBTable.Fields[i].DataType of
ftString,
ftWideString,
ftFixedChar,
ftFixedWideChar:
if TStringField(IBTable.Fields[i]).FixedChar then
TStringField(IBTable.Fields[i]).FixedChar := False;
end;

 
こんな感じですね。ちなみに DBX4 の場合には TClientDataSet にある DisableStringTrim プロパティを False に設定すれば空白文字を除去した文字列を返してくれます。

話を元に戻しますが、私は VARCHAR() しか使わないのですっかりこの件を忘れておりました。ご指摘ありがとうございます m(_ _)m
この件は次のバージョンで修正しておきますね。

追記: TStringField.FixedChar は ANSI 版 Delphi の IBX では正しく動作しないようです。

DEKO
管理者
投稿数: 2690
Re: IBConsoleの動作について
on: 2015/10/24 21:42 Sat

言うまでもない事かもしれませんが…Delphi でこの件を修正する場合、結果に対して Trim() するのはダメですよ。せめて TrimRight() にしましょう。

修正版をリリースしました (Unicode 版 rel.52 / ANSI 版 rel.88)。
Delphi でアプリケーションを作る場合には空白パディングの方が都合がいい事もある (固定長での処理) と思いますが、IBConsole においては空白パディングされても嬉しい事は (何も) ないので、空白パディングはしないようにしました。

DEKO
管理者
投稿数: 2690
Re: IBConsoleの動作について
on: 2015/10/25 00:02 Sun

IBX は空白パディングの文字数計算を間違っています。
(IBX.)IBCustomDataSet.pas の TIBCustomDataSet.InternalGetFieldData の中ですが、

function TIBCustomDataSet.InternalGetFieldData(Field: TField; Buffer: TValueBuffer): Boolean;
var
Buff: TRecBuf;
Data : PByte;
CurrentRecord: PRecordData;
cFieldData : TFieldData;
begin
...

// fdDatalength represents the internal bytes length which is now Unicode
if cFieldData.fdDataLength div 2 <= Field.Size then
begin
if (Field is TStringfield) and TStringField(Field).FixedChar then
StrPCopy(PChar(Buffer), StringOfChar(' ', Field.Size));
Move(Data^, PByte(Buffer)[0], cFieldData.fdDataLength);
if (Field is TStringfield) and TStringField(Field).FixedChar then
PChar(Buffer)[Field.Size] := #0 // <- バイトサイズで終端している
else
PChar(Buffer)[cFieldData.fdDataLength div 2] := #0;
if (cFieldData.fdDataType = SQL_TEXT) and (not TStringField(Field).FixedChar) then
PChar(Buffer)[Length(TrimRight(PChar(Buffer)))] := #0;
end
else
IBError(ibxeFieldSizeMismatch, [Field.FieldName]);

...
end;

 
文字コードを考慮せずにヌル文字を入れていますね。ANSI 版 IBX (2007 以前) で SJIS_0208 を使っている場合には正しく空白パディングされますが、UNICODE 版 IBX (2009 以降) で SJIS_0208 を使うとこれまた正しく空白パディングされません。

ただ、これが簡単に直るかと言うと、それにはちょっと疑問が残ります。何故ならば Firebird の文字コードと Windows の文字コードが 1:1 で対応しないからです。例えば UNICODE_FSS という文字コードは Windows には存在しません。UTF-8 はありますが、UTF8 は 4 バイトの UTF-8 であるのに対し、UNICODE_FSS は 3 バイトの UTF-8 なので、簡単には変換できません (少なくとも Windows が持っている機能だけでは変換できません)。

DEKO
管理者
投稿数: 2690
Re: IBConsoleの動作について
on: 2015/10/25 01:45 Sun

あ…いやいや、コレおかしくなるのは UTF8 を指定した時だけですね。UNICODE_FSS なら大丈夫です。
http://www.freeml.com/delphi-users/3462/latest

普通なら、Field.Size には VARCHAR() で指定した "文字数" が入っているのに、UTF8 の時だけバイト数が入ります。

DEKO
管理者
投稿数: 2690
Re: IBConsoleの動作について
on: 2015/10/25 03:08 Sun

(IBX.)IBSQL.pas を変更すれば IBX でも Firebird の UTF8 が正しく使えるようになるようです。
IBConsole の最新版 (Unicode 版 rel.53 / ANSI 版 rel.89) をリリースしました。

DEKO
管理者
投稿数: 2690
Re: IBConsoleの動作について
on: 2015/10/25 06:40 Sun

ちょっと整理します。ROMG さんご指摘の件には2つの問題点がありました。

1.CHAR() フィールドだと空白パディングされる。
Unicode 版 IBX だと TStringField.FixedChar を False に設定する事で回避可能。

2.UTF8 だと空白を消さないと登録時にエラーになる。
InterBase と Firebird で UTF-8 の文字コード ID が異なるのが原因。Unicode 版 IBX なら (IBX.)IBSQL.pas を修正する事で回避可能。

…という事で、IBConsole Unicode 版では回避できました。IBConsole ANSI 版は残念な結果となりました。

ROMG
メンバー
投稿数: 2
Re: IBConsoleの動作について
on: 2015/10/25 20:22 Sun

ROMGです。
DEKOさんの早速の対応に感謝します。

Char(n)定義の場合、文字列長がnに足りない場合、空白で埋められるのは承知していましたがゴミ付きではTrimRight()を単純に
適用できないなぁ…と思っていました。
ちなみに、ここにアップした後でD2007を使ってサンプルデータを表示してみました。IBXのバージョンは11.11(IBXConst.pas)です。
IBDatabase->IBTransaction->IBQuery->DataSource->DBgridと単純に接続して表示してみたところ、
Char(n)の文字数不足部には空白が埋められていますが、ゴミはありませんでした(TrimRight()なしで)。

いずれにしても今回はDBの文字コードとIBConsoleが使用しているIBXがGrid上へ表示する上の問題であり、Qrueryを投げて帰ってくる
文字列は(当然のことですが)文字数不足分のみを空白で埋めていると考えてよいので、アプリ作成はこのことさえ考慮していれば問題ない
と考えます。

(蛇足)
小生は趣味として純粋に個人的なDBソフトを作成、楽しんでおり、他人様に使ってもらうなどはさらさら考えていません。
データ数十万件のDBを幾つか作成していますが、マスターテーブル(数十~百数十件程度)の作成、メンテなどはいつもDEKOさんの
IBConsoleのお世話になっており、今回の確認のお願いになりました。
(個人で使えるフリーのDBマネージャソフトを幾つか使ってみましたが、結局IBConsoleに落ち着き、今では他は全て捨てました)
Delphiをバージョンアップしたら(いつか出来たら)FireDACなるものへの移行もありかと、D2007にインスト可能と唱ったものをDLし
インストールにチャレンジしましたが、エラー(ソースの一部が文字化けしている)で今は投げ出したままです。

ページ: [1]
WP Forum Server by ForumPress | LucidCrew
バージョン: 1.7.5 ; ページロード: 0.053 sec.