Yet another OSS DB:Firebird(5)

内部関数が足りない? それならUDFだ!

Firebird日本ユーザー会
アナハイムテクノロジー
はやしつとむ
2009/6/9

UDFに文字を渡す

 次に、UDFに文字型のデータを渡すコードを見てみましょう。同じくIB_UDFのソースコードからです。

ib_udf.cpp(81)
ISC_LONG EXPORT IB_UDF_ascii_val( const char *a)
{
    // NULL is treated as ASCII(0).
    return ((ISC_LONG) (*a));
}

 IB_UDF_ascii_val()関数は、引数で指定されたASCII文字の文字コードを返す関数です。char型のポインタaで参照される文字をint型にキャストして返しているだけです。シンプルですね。

 同じことをDelphiで書くと以下のようになります。

function tomneko_ascii_val(var a:char):Integer;stdcall;
begin
  result := Ord(a);
end;

●登録用スクリプト:

/*  External Function declarations */

DECLARE EXTERNAL FUNCTION TOMNEKO_ASCII_VAL
CHAR(1) CHARACTER SET ASCII
RETURNS INTEGER BY VALUE
ENTRY_POINT 'tomneko_ascii_val' MODULE_NAME 'UDF_sample';

●使用例:

SQL> SELECT TOMNEKO_ASCII_VAL('a') FROM RDB$DATABASE;
TOMNEKO_ASCII_VAL
=============
           97

UDFに文字列を渡す

 ascii_val()関数のように、文字列を引数として受け取るような場合は問題とならないのですが、文字列を戻り値として返すために、関数の内部で文字列処理のメモリ領域を用意するような場合は注意が必要です。

 ご存じのように、Firebirdのスーパーサーバ版はマルチスレッド化されているため、複数の接続に対して、1プロセスのFirebirdサーバが複数のスレッドを生成して処理を行っています。このとき、各スレッドはメモリ空間を共有しているため、UDFの呼び出しにおいて同じ関数が別のスレッドから同時に呼ばれてしまった場合、静的変数の内容が上書きされてしまうなどの再入問題が発生する可能性があります。

 ところが、動的にメモリ領域を確保するとなると、誰がそれを解放するのかという問題が生じます。UDF内部で確保されたメモリ領域を使って、戻り値をFirebirdへ渡す場合、Firebirdがそのメモリ領域を解放しなくてはならないからです。

 この問題の解決方法として、Firebirdには、ib_util.dll(Linuxではib_util.so)というライブラリが付属しています。これはUDF内で動的にメモリを確保するために使用されます。このライブラリを使用することで、呼び出し側であるFirebirdがUDF側で確保したメモリを解放することが可能となります。

 この場合、UDF登録用スクリプトのREURNS句の最後にFREE ITキーワードを指定します。このFREE IT指定によって、FirebirdはUDFの呼び出しが終了した時点で、不要となったメモリ領域をib_util_malloc()と同じメモリマネージャを利用して解放します。

 しかし、ib_util_malloc()関数とFREE ITキーワードを利用する方法は、あまりスマートとはいえません。そこで、もう1つ方法を紹介します。

 DECLARE EXTERNAL FUNCTION文のRETURNS句で戻り値のデータ型を指定する代わりに、PARAMETER句を指定することで、引数として渡すデータの中から戻り値を指定することができます。具体的には、以下のようにします。

procedure tomneko_ascii_char2(var a:Integer; b:PChar);stdcall;
begin
  b^ := Char(a);
  (b+1)^ := #0;
end;

●登録用スクリプト:

DECLARE EXTERNAL FUNCTION tomneko_ascii_char2
INTEGER, 
CSTRING(1) CHARACTER SET ASCII
RETURNS PARAMETER 2 
ENTRY_POINT 'tomneko_ascii_char2' MODULE_NAME 'UDF_sample';

●使用例:

SQL> select tomneko_ascii_char2(97) from rdb$database;
TOMNEKO_ASCII_CHAR2
===============
a

 この場合、戻り値のために必要なメモリ領域は、Firebirdが管理しているので、FREE ITキーワードもib_util_malloc()関数も必要ありません。

前のページへ 3/4 次のページへ

Index
内部関数が足りない? それならUDFだ!

Page 1
内部関数で物足りないあなたに
UDFの仕組み
標準UDFのコード

Page 2
引数を取らないUDF
数値型の引数を取るUDF
→ Page 3
UDFに文字を渡す
UDFに文字列を渡す

Page 4
最後に
Yet another OSS DB:Firebird


Database Expert フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Database Expert 記事ランキング

本日月間