内部関数が足りない? それならUDFだ!
アナハイムテクノロジー
はやしつとむ
2009/6/9
内部関数で物足りないあなたに
第4回「Firebirdの標準関数を極める」は、バージョン2.1で大きく変更されたFirebirdの内部関数について解説しました。かなりの数の内部関数が取り込まれて充実したFirebirdですが、もちろんこれだけでは不足する場合があります。そのため、Firebirdではユーザー定義関数(UDF)という仕組みが用意されていて、誰でも簡単に関数を作成して追加することができるようになっています。それでは、詳しく見ていきましょう。
UDFの仕組み
UDFをFirebirdで利用するための仕組みとして、DECLARE EXTERNAL FUNCTION文が用意されています。入手・作成したUDFは、このステートメントで各データベースへ登録することで、内部関数と同じように各種のSQL文で利用することができるようになります。
【DECLARE EXTERNAL FUNCTION文のSyntax】
DECLARE EXTERNAL FUNCTION ローカル関数名 [<タイプ定義> [, <タイプ定義> ...]] RETURNS {<リターンタイプ定義> | PARAMETER パラメータ位置} [FREE_IT] ENTRY_POINT '関数名' MODULE_NAME 'DLL/SOファイル名' <タイプ定義> ::= sqltype [BY DESCRIPTOR] | CSTRING(文字列長) <リターンタイプ定義> ::= sqltype [BY {DESCRIPTOR|VALUE}] | CSTRING(文字列長)
- ローカル関数名は、関数名のエイリアスなので自由に(Firebirdのオブジェクト命名規則の範囲内で)付けてよい
- CSTRING型の場合は、長さを1〜(32767 ÷ 1文字の最大bytes長)で指定します。1文字の最大bytes長は各キャラクタセットで違いますが、SJIS_0208なら2bytes、UTF8なら4bytesです。
標準UDFのコード
それではまず、標準UDFに用意された簡単な関数のコードを追っていくところから始めたいと思います。いまでは使われなくなったとはいえ、ソースコードはそのまま残っているので、これをサンプルにして学ぶのが近道です。
以下に示すのは、Firebird-2.5.0.23247-Beta1.tar.bz2を展開して、src\extlibに含まれているib_udf.cppのソースコードの一部です。大変簡潔なので、UDF作成が手軽にできることをお分かりいただけると思います。
ib_udf.cpp(284)
double EXPORT IB_UDF_srand() { srand((unsigned) time(NULL)); return ((float) rand() / (float) RAND_MAX); }
一見して分かるとおり、IB_UDF_srand()関数は引数を取らず、戻り値がdouble型となっています。
1行目で、srand()関数によって乱数系列を初期化しています。RAND_MAXはstdlib.hで定義されている定数値で、rand()関数が0〜RAND_MAXの範囲の乱数を返すため、RAND_MAXで割ることで0-1の範囲の乱数を返すということになります。
問題は、IB_UDF_srand()関数の呼び出しを行うたびに、srand()関数が呼ばれることにあります。WindowsのようにOSのタイマの精度が低い場合、連続した関数呼び出し中でtime()関数が同じ値を返してしまい、結果としてrand()関数が同じ値を返すことになってしまいます。
そこで、Firebird2.1で標準UDFのほとんどを内部関数化した際に、Posix版では/dev/urandomデバイスを利用し、Windows版ではCryptGenRandom APIを利用するように書き換えられました。そもそもCの標準関数であるrand()は十分な乱数特性がないので、現在ではより高度なアルゴリズムで乱数を生成するべきだという認識が広がっています。
guid.cpp(41) void GenerateRandomBytes(void* buffer, size_t size) { HCRYPTPROV hProv; // Acquire crypto-provider context handle. if (! CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { if (GetLastError() == NTE_BAD_KEYSET) { // A common cause of this error is that // the key container does not exist. // To create a key container, call CryptAcquireContext // using the CRYPT_NEWKEYSET flag. if (! CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET)) { Firebird::system_call_failed::raise("CryptAcquireContext"); } } else { Firebird::system_call_failed::raise("CryptAcquireContext"); } } if (! CryptGenRandom(hProv, size, static_cast<UCHAR*>(buffer))) { Firebird::system_call_failed::raise("CryptGenRandom"); } CryptReleaseContext(hProv, 0); }
1/4 |
Index | |
内部関数が足りない? それならUDFだ! | |
Page 1 内部関数で物足りないあなたに UDFの仕組み 標準UDFのコード |
|
Page 2 引数を取らないUDF 数値型の引数を取るUDF |
|
Page 3 UDFに文字を渡す UDFに文字列を渡す |
|
Page 4 最後に |
Yet another OSS DB:Firebird |
- Oracleライセンス「SE2」検証 CPUスレッド数制限はどんな仕組みで制御されるのか (2017/7/26)
データベース管理システムの運用でトラブルが発生したらどうするか。DBサポートスペシャリストが現場目線の解決Tipsをお届けします。今回は、Oracle SE2の「CPUスレッド数制限」がどんな仕組みで行われるのかを検証します - ドメイン参加後、SQL Serverが起動しなくなった (2017/7/24)
本連載では、「SQL Server」で発生するトラブルを「どんな方法で」「どのように」解決していくか、正しい対処のためのノウハウを紹介します。今回は、「ドメイン参加後にSQL Serverが起動しなくなった場合の対処方法」を解説します - さらに高度なSQL実行計画の取得」のために理解しておくべきこと (2017/7/21)
日本オラクルのデータベーススペシャリストが「DBAがすぐ実践できる即効テクニック」を紹介する本連載。今回は「より高度なSQL実行計画を取得するために、理解しておいてほしいこと」を解説します - データベースセキュリティが「各種ガイドライン」に記載され始めている事実 (2017/7/20)
本連載では、「データベースセキュリティに必要な対策」を学び、DBMSでの「具体的な実装方法」や「Tips」などを紹介していきます。今回は、「各種ガイドラインが示すコンプライアンス要件に、データベースのセキュリティはどのように記載されているのか」を解説します
|
|