本日はA5:SQL Mk-2でも利用している、データベースアクセスコンポーネント、Devart社のUniDACコンポーネントのご紹介をしたいと思います。これは有償のコンポーネントなのですが、とても便利でユニークな機能を備えたデータベースアクセスコンポーネントです。
開発者ライセンス・チームライセンス・サイトライセンスがあり、ロイヤリティフリーで使用できます。
32bit,64bitに対応し、Delphi, C++ Builder, Lazarusに対応し、Oracle Database、Microsoft SQL Server、MySQL、InterBase、Firebird、PostgreSQL、SQLite、IBM DB2、Microsoft Access、SAP Sybase Advantage Database Serverなどに接続できます。(できるそうです。)
個人的に一番便利だと感じている機能として、ダイレクトモードによるデータベースごとのクライアントライブラリの要らないデータベース接続です。
ダイレクトモードに対応したデータベースとしては、Oracle Database, Microsoft SQL Server, MySQL, PostgreSQL,SQLite等があります。ダイレクトモードでこれらのデータベース接続するとき、Oracle Client (OCI)も、Native Clientも、libmysql.dllもlibpq.dllもsqlite3.dllも必要ありません。
Oracle接続するのに、Oracle Client要らないってのは結構びっくりですよね。あと、MySQLへ接続するのにlibmysql.dll (GPL) が必要ないので、GPLの縛りなくMySQLに接続できるというのが大きいです。
最低限、TUniConnectionとTUniQueryを抑えれば、SELECT, INSERT, UPDATE, DELETEができます。TUniQueryは、もちろんTDataSetを継承しています。
こんな感じ…。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var UniConnection: TUniConnection; UniQuery: TUniQuery; begin // Oracle OCI接続のとき UniConnection := TUniConnection.Create(nil); UniConnection.ProviderName := OracleUniProvider.TOracleUniProvider.GetProviderName; // Oracleへの接続 UniConnection.Server := 'ServerName:1521/ORCL'; // Oracleの簡易接続 UniConnection.SpecificOptions.Values['Direct'] := 'False'; // OCIを使った接続 UniConnection.SpecificOptions.Values['UseUnicode'] := 'True' // Unicodeを使った接続(基本Trueがよいです) UniConnection.Options.EnableBCD := True; // BCD型を使う(多分Trueのほうが何かと良いと思う) UniConnection.Options.EnableFMTBCD := True; // FmtBCD型を使う(多分Trueのほうが何かと良いと思う) UniConnection.Open; … |
Oracleダイレクトモードのときは以下
1 2 3 4 5 6 7 8 |
UniConnection := TUniConnection.Create(nil); UniConnection.ProviderName := OracleUniProvider.TOracleUniProvider.GetProviderName; // Oracleへの接続 UniConnection.Server := 'ServerName:1521:ORCL'; UniConnection.SpecificOptions.Values['Direct'] := 'True'; // OCIを使わない接続 UniConnection.SpecificOptions.Values['UseUnicode'] := 'True' // Unicodeを使った接続(基本Trueがよいです) UniConnection.Options.EnableBCD := True; // BCD型を使う(多分Trueのほうが何かと良いと思う) UniConnection.Options.EnableFMTBCD := True; // FmtBCD型を使う(多分Trueのほうが何かと良いと思う) UniConnection.Open; |
MySQLなら以下(MySQLはダイレクトモードのみ)
1 2 3 4 5 6 7 8 9 10 11 12 |
UniConnection := TUniConnection.Create(nil); UniConnection.ProviderName := TMySQLUniProvider.GetProviderName; // MySQLへの接続 UniConnection.Server := 'ServerName'; UniConnection.Port := 3306; UniConnection.Database := 'DB_NAME'; UniConnection.UserName := 'username'; UniConnection.Password := 'password'; //UniConnection.SpecificOptions.Values['Direct'] := 'True'; // MySQLは指定してもしなくてもダイレクトモードになる UniConnection.SpecificOptions.Values['UseUnicode'] := 'True' // Unicodeを使った接続(基本Trueがよいです) UniConnection.Options.EnableBCD := True; // BCD型を使う(多分Trueのほうが何かと良いと思う) UniConnection.Options.EnableFMTBCD := True; // FmtBCD型を使う(多分Trueのほうが何かと良いと思う) UniConnection.Open; |
クエリーの実行
1 2 3 4 5 6 |
UniQuery := TUniQuery.Create(nil); UniQuery.Connection := UniConnection; UniQuery.UniDirectional := False; // 双方向結果セットが欲しいときはFalse、バッチなどで片方向でよいときはTrue UniQuery.Options.QuoteNames := True; // 更新可能な結果セットのとき、内部でDMLが作られるが、クォート文字で列名をくくる。…予約語と被るようなカラム名があるときはTrueにする UniQuery.SQL.Text := 'SELECT * FROM TABLE_NAME'; UniQuery.Open; |
MySQLでのクエリー実行は以下のオプションも追加
1 2 |
// MySQLの追加のオプション(UniQuery.Openの前に) UniQuery.SpecificOptions.Values['EnableBoolean'] := 'False' // これをしないと MySQLのtinyint(1)がBoolean扱いになってしまう。 |
PostgreSQLでのクエリー実行は以下のオプションも追加
1 2 |
// PostgreSQLの追加のオプション(UniQuery.Openの前に) UniQuery.SpecificOptions.Values['OIDAsInt'] := 'True' // これをしないと OID型がうまく表示できない |
基本的にDB接続ではものすごいお役立ちコンポーネントなのですが、ちょいちょい不具合などもあるようです。UniDACのライセンスを購入すると、ソースコードが付いてくるので、A5:SQL Mk-2では以下の不具合修正やカスタマイズを行って利用しています。
利用される際はこれらの点に気を付けて使うとよいかと思います。修正箇所はDevart社の著作物に関わるところなのでここでは公開できませんが、連絡いただければご相談に乗れるかもしれません。
■不具合関連
・DB2で、ODBCドライバ名が「IBM DB2 ODBC DRIVER」でない場合もつなげるように修正
UniDACではDB2への接続にODBCドライバを使いますが、この時のODBCドライバの名前が決め打ちで記述されています。これだと、最近のバージョンのDB2では接続できなかったりするので、DB2接続前にODBCドライバ名を動的に取得するように修正しています。
・固定長文字列型で、更新時は”にならない限りTrimして更新する対応
CHAR(x)などの固定長文字列では、文字コードセットによって’あ ‘を2文字として扱うか、3文字(以上)として扱うかが変わってきてしまいます。このため、Insert, Update時には文字列をTrimして保存することで、内部的に文字列が長すぎると判定される問題を回避します。ただし、元の文字列が ‘ ‘ 等ではTrimすると、”となってしまいますが、この時はTrimしません。
・SJISのCHAR(2)型で、’あ’が ‘あ ‘になってしまうのを防ぐ
上と同じような話ですが、CHAR(2)のデータ型を取得したときに’あ’があると「2文字」に拡張されて’あ ‘になって表示されるのを防ぎます。
・MySQLでプロシージャのパラメータが取得できない不具合の修正
MySQLのプロシージャのパラメータを取得しようとすると、スキーマ名(データベース名)が必要なところで記述されていないため、うまく取得できない不具合があります。これを修正して使っています。
・MySQLでbigint unsignedの12345678901234567890が -6.10106517247498E18 とかになる不具合修正 (MyClassesUni.pas)
内部でMySQLの型からDelphiのデータ型にマッピングするときの問題でbigint unsignedの値がうまく扱えないことがあります。64bitの符号付整数で扱えない型はBCD型で扱うように修正しています。
・MySQLでエラー文字化け対応
MySQLの文字コードの取り扱いがうまくないので日本語のエラーメッセージが文字化けしてしまいます。これを修正して使っています。
・Oracleへの直接接続で、セミコロンをSQL中に含むステートメントを実行できない問題修正
Oracleへのダイレクトモードでの接続でSQL中にセミコロンがあるようなSQLを実行できません。上述のOraNetUni.pasを修正すればよいのですが、OracleへOCIなしで接続する機能は虎の子なのか、ソースは付属するものの、きっちり難読化されていてほとんど手出しできません…何とか直しました。一般的なアプリケーションなら、単純にセミコロンを含むSQLを発行しないようにしたほうが早いと思います。
・Oracleへの直接接続で、エラーメッセージを日本語で受け取ると、メッセージが途切れる不具合を修正
英語圏のプログラムでありがちなマルチバイト文字が通らない系の不具合です。文字数とバイト数を混同して扱っている箇所があるためです。例によって難読化されているソースコードなので、修正が大変です。エラーメッセージは英語で扱ったほうが良いと思います。(NLS_LANG=AMERICAN_AMERICA.WE8MSWIN1252 とか)
・SQLServerでパラメータとして日付型データが渡せない不具合を修正
SQLでパラメータとして日付型(時刻型・日付時刻型も)を渡すときなんか、内部で扱っているポインタがTDateTimeだったり文字列だったり混同しているようです。
■機能追加・修正関連
・レコード件数制限 (MaxRecords)
DelphiのADO接続の標準コンポーネントTADOQueryにはMaxRecordsのようなプロパティがありますが、UniDACにはありません。このため、MaxRecordsに相当する機能を追加して使っています。
・SQLiteの自動インクリメント型表示対応
SQLiteの自動インクリメント型に対応していないので、対応させています。
・MySQLの0000-00-00対応
MySQLには 0000-00-00という変な日付があります。A5:SQL Mk-2ではこれを扱えるように修正しています。…ただし、0001/01/01 が扱えなくなります…。ただ、これはUniDACの問題というより、MySQLが0000-00-00というおかしな日付を持つのがよくないと思います。0000-00-00という日付を使うくらいなら、NULLを使うのが正解かと。
・–コメントを/**/コメントに書き換えているロジックを削除…Oracleとかでエラー位置が正しく扱えなくなってしまう問題の対処
これは大抵のアプリケーションでは問題とならないのですが、なぜか UniDACでは — 形式のコメントを内部的に /**/のコメントに変換してからDBサーバーに送ります。…なんでそんなことするんですか?。A5:SQL Mk-2ではSQLにエラーがあった場合、エラー位置を表示したりしますが、 SQLを内部で書き換えていると、エラー位置がずれてしまうので面白くありません。この機能は無効化しています。
・MySQLでJSON型対応
UniDACの最近のバージョンでようやくMySQLのJSONに対応しました。A5:SQL Mk-2では独自に対応していました。
・MySQLでTEXT型を普通の文字列として扱えるように
MySQLのTEXTは通常ftMemo型として扱われますが、ftStringとしても扱えるように修正しています。
・OracleでOCI(のDLL)の初期化に失敗したときのエラーメッセージをわかりやすく
アーキテクチャ(32bit/64bit)が異なるoci.dllをロードしようとしてしまったとき、単に初期化に失敗しただけではわからないことがあるので、ビット数が異なることを明示するエラーメッセージにしています。
・PostgreSQLでSQL_ASCIIのデータベースで強制的にエンコーディングを指定できるようにする。
PostgreSQL でSQL_ASCIIのデータベースが文字化けしてしまう問題に対処するため、エンコーディングを指定できる機能を追加しています。
・PostgreSQLで0100/01/01より前の日付を扱えない問題の修正
なぜか、PostgreSQLで0100/01/01より前の日付を弾くチェックロジックがあるのでコメントアウトして使ってます。
・PostgreSQL 7.2 対応(主にv7.2ではスキーマに対応していないことの対応)(PgClassesUni.pas)
標準の状態ではPostgreSQL 7.2に対応しないので修正しています。具体的にはPostgreSQL 7.2はスキーマに対応しないので、システムテーブルを扱うときスキーマを扱っている箇所をスキーマを使わないように修正しています。
・PostgreSQLでTEXT型を普通の文字列として扱えるように(PgClassesUni.pas)
TEXT型は通常だと、ftMemo型として扱われますが、ftStringにマッピングできるように修正しています。
・SQLiteでTEXT型を普通の文字列として扱えるように(LiteClassesUni.pas)
TEXT型は通常だと、ftMemo型として扱われますが、ftStringにマッピングできるように修正しています。
・SQLServerでMoney型を通貨型ではなくBCDとして扱うように修正(特定の通貨である前提はないから…OSの設定により、勝手に\とかついてしまう対処)(MSClassesUni.pas)
Money型はOS設定から通貨記号や小数点の丸め処理が行われますが、外国の通貨を扱ったりすることもあるので、BCD型として扱えるようにしています。
■その他
・MySQLやPostgreSQLではそうでもない気がしますが、Oracleのダイレクトモード接続は変わったデータ型とか使ったりOracleの新しい機能を使ったりすると、チョイチョイ不具合を起こしがちな気がします。おそらくOracleのプロトコル(Net8)自体公開はされていないので、Devart社が独自に解析しているため、追従しきれないところがあるのではないかと思うのですが、可能ならダイレクトモードではなく、OCIを経由して接続したほうがトラブルが少ない気がします。
・SQLiteはデータの暗号化もサポートします。ただし、使うライブラリごとに暗号化方式は互換性がありません。UniDACでも暗号化はできるのですがUniDAC以外の暗号方式とは互換性がないため、利用には注意が必要です。