S-JIS[2025-06-14]
|
ODBCドライバーを実装する(自作する)ということは、ODBCで定義されている(C言語の)関数を実装してライブラリー(Windowsならdllファイル)を提供するということである。
そういう意味では通常のライブラリーを作るのと変わらないが、
ODBCの場合は、自作ライブラリーの関数がアプリケーションから直接呼ばれるのではなく、ODBCドライバーマネージャー(Windowsの場合はodbc32.dll)経由で呼ばれるという点が異なる。
ODBCのアプリケーションでは、以下の順序でハンドルを生成してDBに接続する。
ODBCドライバーもSQLAllocHandle()を実装し、自分のライブラリー内で使用するインスタンスを生成する。
ここで疑問なのは、SQLDriverConnect()でドライバー名を指定するので、そこで初めて自分のODBCドライバーがロードされはずであり、すると、SQLAllocHandle()の呼び出しはどうなってるんだ?ということ。
実際には、おそらく以下のように動作している。
(ここでは、ODBCドライバーマネージャーをDMと略す)
つまり、SQLAllocHandle()の呼び出しは、どのODBCドライバーが使われるかが決定するまで遅延されるイメージ。
もしODBCドライバーのSQLAllocHandle(ENV)やSQLAllocHandle(DBC)でエラーが発生したら、アプリケーションにはSQLDriverConnect()のエラーとして伝わることになる。
実際、アプリケーションが受け取るhEnvやhDbcの値は、ODBCドライバーが返すhEnvやhDbcの値(構造体のアドレス)とは異なっている。
アプリケーションはODBCドライバーマネージャーから受け取ったhEnvやhDbcを使って関数を呼び出すが、
ODBCドライバーマネージャーは、内部で保持している「ドライバー用のhEnvやhDbc」を使ってODBCドライバーの関数を呼び出すのだろう。
ODBCの関数の中には、関数名の末尾にAやWが付いているものがある。
ODBC関数の引数で文字列を扱うものについては、何も付いていない関数(無印)と、A付き・W付きがある。
引数で直接文字列を扱わない関数には、A・Wが付く関数は無い。
AはANSI版とのことで、文字の型はSQL_CHAR(8ビット)。
文字列は基本的にASCIIを扱う。
実際には環境依存らしく、Windowsの場合はShift-JIS(おそらくMS932)で扱われるようだ。
WはWideChar版で、文字の型はSQL_WCHAR(16ビット)。
文字列はUTF-16(リトルエンディアン)で扱う。
(ちなみに、JavaのUTF-16はビッグエンディアンで、リトルエンディアンはUTF-16LE)
アプリケーションから呼ぶ場合、無印とA付きの関数は同じ挙動となる。(文字をSQL_CHARで扱う)
日本語を扱う場合は、アプリケーションからW付きの関数を呼ぶ。(文字をSQL_WCHARで扱う)
アプリケーションからAやWの関数を呼んでも、そのままODBCドライバーのAやWの関数が呼ばれるわけではない。
ODBCでは、アプリケーションからはODBCドライバーマネージャーの関数が呼ばれる。
そして、ODBCドライバーのW付きと無印の関数のどちらを呼ぶかは、ODBCドライバーマネージャーが決めるようだ。
したがって、アプリケーションからA付きの関数を呼んでもODBCドライバーのW付き関数が呼ばれるかもしれないし、
アプリケーションからW付きの関数を呼んでもODBCドライバーの無印関数が呼ばれるかもしれない。
(SQL_CHAR⇔SQL_WCHARの変換は、ODBCドライバーマネージャーがよしなに実施してくれる)
なお、ODBCドライバーのA付きの関数は呼ばれることが無いようだ。
(つまり、ODBCドライバーはA付きの関数を実装する必要は無い)
すなわち、ODBCドライバーとしては、無印の関数をANSI用として実装し、同時にW付きの関数を提供する。
ODBCの関数には、属性の設定・取得を行う関数がある。
「属性なんかはデフォルト値でいいだろうから、実装は後回しにしよう…」と思うところだが、必須の属性があったりするので、早めに実装した方が良い。
属性は色々あるので、引数をデバッグログ出力しておき、呼ばれた属性について実装するのが良さげ。
ODBCの関数内でエラーが発生した場合、SQLGetDiagRec(W)関数でエラーコードやエラーメッセージを取得できるようにする。
SQLGetDiagRec()の引数はハンドルになっており、hEnvやhDbc・hStmt等を指定する。
つまり、ハンドルの種類ごとにエラー情報を保持する。
あるハンドルを使う関数が呼ばれたら、まずそのハンドル内のエラー情報をクリアする。
で、関数内でエラーが発生したら、SQLGetDiagRec()で返せるようにエラー情報を構築・保存する。
エラー情報は複数保存・取得できるようにする必要がある。
ODBCドライバーの関数を実装するには、Diag系関数を除いて、以下の順に作っていくのがいいと思う。
(Diag系関数はエラーメッセージを返すので早めに実装した方がいいが、EnvやDbcの実装と密接に関係するので、その前後が良さそう)
ここまで実装すれば、アプリケーションからSQLDriverConnect()が呼び出せる。
次いで、hStmt(ステートメント)を扱う関数を実装する。
後は、SQLTables()やSQLExecDirect()といった関数を実装していく。