[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13. MySQL の拡張

13.1 MySQL の内部情報   
13.2 MySQL への新しい関数の追加   
13.3 MySQL への新しいプロシージャの追加   


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.1 MySQL の内部情報

この章では、MySQL コードを扱うときに知る必要がある事項について説明します。MySQL の開発に貢献したい、開発中の非公式バージョンのコードにアクセスしたい、あるいは開発過程に興味がある、という場合は、 「2.3.4 開発ソースツリーからのインストール」 の指示を参照してください。MySQL の内部情報に興味がある場合は、当社の internals メーリングリストに加入してください。このメーリングリストはそれほど投稿数は多くありません。加入方法の詳細については、 「1.7.1.1 MySQL メーリングリスト」 を参照してください。MySQL AB の開発者は全員 internals リストに加入しており、MySQL コードに取り組んでる人々をサポートします。コードについて質問したり、MySQL プロジェクトに提供したいパッチを送信する場合には、このリストを遠慮なくご使用ください。

13.1.1 MySQL のスレッド   
13.1.2 MySQL テストスイート   


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.1.1 MySQL のスレッド

MySQL サーバが作成するスレッドを以下に示します。

mysqladmin processlist は、接続スレッド、INSERT DELAYED スレッド、およびレプリケーションスレッドのみを表示します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.1.2 MySQL テストスイート

最近までは、全範囲を対象とする当社のテストスイートは独自に開発した顧客データを使用しており、そのために公開されていませんでした。テストプロセスの中で唯一公開されていたのが、sql-bench ディレクトリにある Perl DBI/DBD ベンチマークの crash-me テスト、および tests ディレクトリにあるその他の諸々のテストでした。標準化された公開テストスイートがなかったため、当社製品のユーザや開発者は、MySQL コードに対して回帰試験を実施することが困難でした。この問題を解決するために、バージョン 3.23.29 から、Unix のソースディストリビューションおよびバイナリディストリビューションに、新しく作成したテストシステムを追加しました。このテストは Unix で実行できます。また、サーバを Cygwin でコンパイルした場合は、Cygwin 環境の Windows 上で実行できます。ネイティブな Windows 環境では現在は動作しません。

現在のテストケースは MySQL 全体をテストするわけではありませんが、SQL を処理するコードにおける明白なバグや OS/ライブラリの問題は検出できるはずです。またレプリケーションのテストは徹底的に行っています。最終的には、コードを 100% カバーするようなテストを作成することを目標としています。そのためにテストスイートを拡充するためのみなさんからのご協力をお待ちしています。特に、お使いのシステムにとってクリティカルな機能を検査するテストをご提供していただければ、今後リリースされるすべての MySQL バージョンでお使いのアプリケーションの動作が保証されることになります。

13.1.2.1 MySQL テストスイートの実行   
13.1.2.2 MySQL テストスイートの拡張   
13.1.2.3 MySQL テストスイートのバグの報告   


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.1.2.1 MySQL テストスイートの実行

テストシステムは、テスト言語インタープリタ(mysqltest)、すべてのテストを実行するシェルスクリプト(mysql-test-run)、テスト専用言語で記述された実際のテストケース、および予想されるテスト結果から構成されます。ビルドしてからシステムでテストスイートを実行するには、ソースルートから make test または mysql-test/mysql-test-run と入力します。バイナリディストリビューションをインストールした場合は、cd でインストールルートディレクトリ(/usr/local/mysql など)に移動し、scripts/mysql-test-run を実行します。すべてのテストが正常終了する必要があります。正常終了しないケースがある場合、その原因を調べ、それが MySQL のバグであれば問題点を報告してください。 「13.1.2.3 MySQL テストスイートのバグの報告」 節 参照 。

テストスイートを実行しようとするマシン上で mysqld が動作している場合、それが 9306 および 9307 のポートをどちらも使用していないのであれば、それを停止する必要はありません。どちらかのポートが使用されている場合は、mysql-test-run を編集してマスタポートまたはスレーブポート、あるいはその両方を、使用できるポートに変更する必要があります。

mysql-test/mysql-test-run test_name を使用して、個々のテストケースを 1 件ずつ実行できます。

あるテストが失敗した場合、--force オプションを指定して mysql-test-run を実行し、ほかにも失敗するテストがあるかどうかを調べる必要があります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.1.2.2 MySQL テストスイートの拡張

mysqltest 言語を使用して独自にテストケースを作成できます。残念ながら、その完全なマニュアルはまだ作成されていません。ただ、現在のテストケースを参考にすることができます。独自にテストケースを作成する際の注意点を以下に示します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.1.2.3 MySQL テストスイートのバグの報告

お使いの MySQL バージョンがテストスイートを正常終了できない場合、以下の処理を実行する必要があります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2 MySQL への新しい関数の追加

MySQL に新しい関数を追加する方法は 2 つあります。

それぞれの方法に長所および短所があります。

どちらの方法を使用して新しい関数を追加する場合も、ABS()SOUNDEX() などのネイティブ関数と同じように使用できます。

13.2.1 CREATE FUNCTION/DROP FUNCTION の構文   
13.2.2 新しいユーザ定義関数の追加   
13.2.3 新しいネイティブ関数の追加   


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2.1 CREATE FUNCTION/DROP FUNCTION の構文

 
CREATE [AGGREGATE] FUNCTION function_name RETURNS {STRING|REAL|INTEGER}
       SONAME shared_library_name

DROP FUNCTION function_name
ユーザ定義関数(UDF)は、ABS()CONCAT() などの MySQL ネイティブ(組み込み)関数と同様に動作する新しい関数を追加して MySQL を拡張するための手段です。

AGGREGATE は、MySQL バージョン 3.23 で新しく追加されました。AGGREGATE 関数は SUMCOUNT() などの MySQL のネイティブグループ関数とまったく同じように動作します。

CREATE FUNCTION は、関数の名前、型、および共有ライブラリ名を mysql.func システムテーブルに保存します。関数を作成および削除するには、mysql データベースに対する INSERT 特権および DELETE 特権を持っている必要があります。

mysqld を起動する際に --skip-grant-tables オプションが指定されていなかった場合、すべてのアクティブな関数はサーバが起動するたびに再ロードされます。オプションが指定されていた場合、UDF の初期化は行われず、それを使用することはできません(アクティブな関数とは CREATE FUNCTION でロードされたが DROP FUNCTION による削除は行われていない関数)。

ユーザ定義関数の作成手順については、 「13.2 MySQL への新しい関数の追加」 を参照してください。UDF のメカニズムが機能するには、関数を C または C++ で記述する必要があります。また、オペレーティングシステムでダイナミックローディングがサポートされていること、および mysqld を(静的ではなく)動的にコンパイルしている必要があります。

注意: AGGREGATE を実行するには、type カラムを含む mysql.func テーブルが必要です。このテーブルがない場合、スクリプト mysql_fix_privilege_tables を実行して、このテーブルを作成する必要があります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2.2 新しいユーザ定義関数の追加

13.2.2.1 単純関数の場合の UDF の呼び出し手順   
13.2.2.2 集計関数の場合の UDF の呼び出し手順   
13.2.2.3 引数の処理   
13.2.2.4 戻り値およびエラー処理   
13.2.2.5 ユーザ定義関数のコンパイルおよびインストール   

UDF メカニズムが機能するには、関数を C または C++ で記述し、オペレーティングシステムでダイナミックローディングがサポートされている必要があります。MySQL ソースディストリビューションには、5 つの関数を定義する `sql/udf_example.cc' ファイルが含まれています。UDF の呼び出し例として、このファイルを参考にしてください。mysqld が UDF 関数を使用できるようにするには、--with-mysqld-ldflags=-rdynamic オプションを指定して MySQL を設定する必要があります。その理由は、多くのプラットフォーム(Linux を含む)では、静的リンクされたプログラムから(dlopen() を使用して)ダイナミックライブラリをロードできます。これは --with-mysqld-ldflags=-all-static を使用している場合でも実行できます。しかし、mysqld からシンボルにアクセスする必要がある UDF を使用する場合(default_charset_info を使用する `sql/udf_example.cc' の metaphone の例を参照)、プログラムを -rdynamic を指定してリンクする必要があるからです(man dlopen 参照)。

プリコンパイルされたバージョンのサーバを使用している場合は、MySQL-Max を使用します。MySQL-Max はダイナミックローディングをサポートしています。

SQL ステートメントで使用する関数には、それぞれ対応する C(または C++)関数を定義する必要があります。以下の説明では、"xxx" をサンプルの関数名として使用します。SQL と C/C++ の区別をするために、XXX()(大文字)は SQL 関数呼び出しを、xxx()(小文字)は C/C++ 関数呼び出しを、それぞれ表すものとします。

XXX() のインタフェースを実装するために作成する C/C++ 関数を以下に示します。

xxx()(必須)
関数のメイン。ここで関数の戻り値を計算する。SQL の型と C/C++ 関数の戻り値の型の対応を以下に示す。

SQL の型 C/C++ の型
STRING char *
INTEGER long long
REAL double

xxx_init()(省略可能)
xxx() の初期化関数。以下の処理を実行できる。

xxx_deinit()(省略可能)
xxx() の後処理関数。初期化関数が割り当てたメモリを解放する必要がある。

SQL ステートメントが XXX() を呼び出すと、MySQL は初期化関数 xxx_init() を呼び出して、引数の確認やメモリ割り当てなどの必要なセットアップを実行させます。xxx_init() がエラーを返した場合、SQL ステートメントの処理はエラーメッセージを出力して中断され、関数のメインと後処理関数の呼び出しは行われません。正常終了した場合、関数 xxx() がレコードごとに 1 回呼び出されます。すべてのレコードの処理が終了したら、後処理関数 xxx_deinit() が呼び出され、必要なクリーンアップ処理を実行します。

集計関数(SUM() など)の場合は、以下の関数も用意する必要があります。

xxx_reset()(必須)
合計値をリセットし、引数を新しいグループの初期値として使用する。
xxx_add()(必須)
引数を既存の合計値に加算する。

集計 UDF を使用する場合、MySQL は以下のように動作します。

  1. xxx_init() を呼び出して集計関数に結果を保存するためのメモリを割り当てる。
  2. GROUP BY 式に従ってテーブルを並べ替える。
  3. 新しいグループの先頭のレコードに対して、xxx_reset() 関数を呼び出す。
  4. 同じグループに属する新しいレコードごとに、xxx_add() 関数を呼び出す。
  5. グループが変わった場合、または最後のレコードの処理が終わった場合、xxx() を呼び出して集計値を取得する。
  6. すべてのレコードの処理が終了するまで 3 から 5 の手順を繰り返す。
  7. xxx_deinit() を呼び出して、UDF に割り当てられていたメモリを解放する。

上記の関数はすべて(関数だけでなく、初期化関数および後処理関数も)スレッドセーフで作成する必要があります。これは、変化する可能性があるグローバル変数やスタティック変数にメモリを割り当てることはできないことを意味します。メモリが必要な場合は、xxx_init() で割り当て、xxx_deinit() で解放する必要があります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2.2.1 単純関数の場合の UDF の呼び出し手順

関数は以下に示すように宣言する必要があります。注意: 戻り値の型およびパラメータは、CREATE FUNCTION ステートメントで SQL 関数 XXX() の戻り値を STRINGINTEGER、または REAL のどの型で返すと宣言するかによって異なります。

STRING 関数の場合は以下のように宣言します。

 
char *xxx(UDF_INIT *initid, UDF_ARGS *args,
          char *result, unsigned long *length,
          char *is_null, char *error);

INTEGER 関数の場合は以下のように宣言します。

 
long long xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);

REAL 関数の場合は以下のように宣言します。

 
double xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);

初期化関数および終了関数は以下のように宣言します。

 
my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);

void xxx_deinit(UDF_INIT *initid);

initid パラメータは、上記の 3 つの関数すべてに渡されます。UDF_INIT 構造体を参照しており、関数間のデータの受け渡しに使用されます。UDF_INIT 構造体のメンバを以下に示します。初期化関数では、必要に応じてメンバの値を変更します(メンバのデフォルト値を使用する場合はそのままにしておきます)。

my_bool maybe_null
xxx_init() は、xxx()NULL を返す可能性がある場合は maybe_null1 に設定する必要がある。maybe_null と宣言されている引数が存在する場合は、デフォルト値は 1 である。

unsigned int decimals
小数点以下桁数。デフォルト値は、関数に渡される引数の最大の小数点以下桁数(たとえば、1.341.345、および 1.3 を渡された場合、1.345 の小数点以下桁数が 3 で一番大きいので、デフォルト値は 3 になる)。

unsigned int max_length
戻り値の文字列表現の最大長。関数の戻り値の型によってデフォルト値は異なる。文字列関数の場合、デフォルトは最長の引数の長さ。整数関数の場合、デフォルトは 21 桁。実数関数の場合、デフォルトは 13 に initid->decimals で示される小数点以下桁数を加算した値(数値関数の場合、この長さには符号や小数点が含まれる)。

BLOB 型の値を返す場合、このメンバの値を 65K または 16M に設定できる。このメモリは割り当てられないが、一時的にデータを格納する必要が生じた場合、どのカラム型を使用するかを判断するために使用できる。

char *ptr
関数が独自の用途に使用できるポインタ。たとえば、関数は initid->ptr を使用して、割り当てられたメモリを関数間で受け渡すことができる。xxx_init() では、以下のようにメモリを割り当て、その値をこのポインタに代入する。

 
initid->ptr = allocated_memory;

xxx() および xxx_deinit() は、initid->ptr を参照してメモリを使用したり、解放します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2.2.2 集計関数の場合の UDF の呼び出し手順

ここでは、集計 UDF 関数を作成する際に定義する必要があるさまざまな関数について説明します。

注意: 以下に示す関数は MySQL 4.1.1 では必要ありません。また使用されることもありません。MySQL 4.0 および MySQL 4.1.1 の両方で動作するようなコードを作成する場合にこれらの関数を定義します。

 
char *xxx_reset(UDF_INIT *initid, UDF_ARGS *args,
                char *is_null, char *error);

この関数は、MySQL が新しいグループの最初の行を見つけたときに呼び出されます。この関数では、内部の集計変数をリセットし、渡された引数をグループの最初の引数として設定する必要があります。

多くの場合、この処理はすべての変数をリセットすることで内部的に実装します(たとえば、xxx_clear() を呼び出してから xxx_add() を呼び出します)。

次の関数は、MySQL 4.1.1 以降でのみ必要です。

 
char *xxx_clear(UDF_INIT *initid, char *is_null, char *error);

この関数は、MySQL が集計結果をリセットする必要があるときに呼び出されます。この関数は新しいグループが始まるたびに呼び出されますが、一致するレコードがなかったクエリの値をリセットするために呼び出される場合もあります。is_null は、xxx_clear() を呼び出す前に CHAR(0) を参照するように設定されます。

何らかの異常が発生した場合、error の参照先に 1 バイトを保存できます。

 
char *xxx_add(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);

この関数は、同じグループに属する 2 番目以降のすべてのレコードについて呼び出されます。この関数では、UDF_ARGS の値を内部集計変数に加算する必要があります。

xxx() 関数は、単純 UDF 関数を定義するときと同じように宣言する必要があります。 「13.2.2.1 単純関数の場合の UDF の呼び出し手順」 節 参照 。

この関数は、グループのすべてのレコードが処理されたときに呼び出されます。通常はこの関数で args 変数にアクセスする必要はまったくなく、内部集計変数に基づいて値を返します。

xxx_reset() および xxx_add() では、引数の処理はすべて、通常の UDF と同じように実行する必要があります。 「13.2.2.3 引数の処理」 節 参照 。

xxx() では、戻り値の処理を、通常の UDF と同じように実行する必要があります。 「13.2.2.4 戻り値およびエラー処理」 節 参照 。

is_null および error へのポインタ引数は、xxx_reset()xxx_clear()xxx_add()、および xxx() への呼び出しですべて同じです。この引数を使用して、エラーが発生したこと、または xxx() 関数が NULL を返す必要があるかどうかを記憶できます。注意: *error には文字列は格納できません。これは 1 バイトフラグです。

is_null は、グループごとにリセットされます(xxx_clear() を呼び出す前でも)。error はリセットされることはありません。

isnull または errorxxx() の後に設定された場合、MySQL はグループ関数の結果として NULL を返します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2.2.3 引数の処理

args パラメータは、UDF_ARGS 構造体を参照します。そのメンバを以下に示します。

unsigned int arg_count
引数の数。作成した関数を呼び出すときに引数の数が決まっている場合は、初期化関数でこの値をチェックする。たとえば、以下のように指定する。

 
if (args->arg_count != 2)
{
    strcpy(message,"XXX() requires two arguments");
    return 1;
}

enum Item_result *arg_type
各引数の型。型を表す値として設定できるのは、STRING_RESULTINT_RESULT、および REAL_RESULT である。

引数が指定した型であることを確認し、一致しない場合にエラーを返すには、初期化関数で arg_type 配列をチェックする。たとえば、以下のように指定する。

 
if (args->arg_type[0] != STRING_RESULT ||
    args->arg_type[1] != INT_RESULT)
{
    strcpy(message,"XXX() requires a string and an integer");
    return 1;
}

関数の引数を特定の型で渡すように要求する代わりに、初期化関数を使用して arg_type の各要素に希望する型を設定する方法もある。これにより、MySQL は、xxx()を呼び出すたびに強制的に引数をその型に変換する。たとえば、最初の 2 つの引数を文字列と整数で渡すように指定するには、xxx_init() で以下の処理を行う。

 
args->arg_type[0] = STRING_RESULT;
args->arg_type[1] = INT_RESULT;

char **args
args->args は、関数が呼び出されたときに渡された引数の一般的な性質について初期化関数に情報を伝達する。i 番目の引数が定数の場合、args->args[i] は引数の値への参照である(値にアクセスする正しい方法については以下の手順を参照)。非定数の場合、args->args[i]0 である。定数引数は、34*7-2、または SIN(3.14) など、定数だけを使用する式である。非定数引数は、カラム名、または非定数引数を使用して呼び出される関数など、レコードごとに変化する可能性がある値を参照する式である。

関数を呼び出すたびに、args->args に、現在処理中のレコードに関して渡される実際の引数が設定される。

関数は i 番目の引数を以下のように参照する。

unsigned long *lengths
初期化関数の呼び出し時は、lengths 配列の要素は対応する引数の最大文字列長を示す。この値は変更せず、そのままにしておく必要がある。関数を呼び出すたびに、lengths に、現在処理中のレコードに関して渡される文字列引数の実際の長さが設定される。(初期化関数の呼び出し時は)引数が INT_RESULT 型または REAL_RESULT 型である場合も、lengths には引数の最大長が格納される。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2.2.4 戻り値およびエラー処理

初期化関数は、エラーがない場合は 0、エラーが発生した場合は 1 を返す必要があります。エラーが発生した場合、xxx_init()message パラメータにヌルで終端されたエラーメッセージを格納する必要があります。このメッセージがクライアントに返されます。メッセージバッファの長さは MYSQL_ERRMSG_SIZE 文字ですが、標準の端末画面の幅に合わせて、80 文字未満でメッセージを作成するように努力する必要があります。

long long 関数および double 関数の場合、関数 xxx() の戻り値が関数値になります。文字列関数の場合、結果を参照するポインタを返し、その文字列長を length 引数に格納する必要があります。

戻り値の内容と長さは、以下のように設定します。

 
memcpy(result, "result string", 13);
*length = 13;

calc 関数には、255 バイトの result バッファが渡されます。返す値がこの大きさに収まる場合は、そのメモリ割り当てについて心配する必要はありません。

文字列関数が 255 バイトより長い文字列を返す必要がある場合、xxx_init() 関数または xxx() 関数で malloc() を使用してそのための領域を割り当て、xxx_deinit() 関数でそれを解放する必要があります。割り当てたメモリを UDF_INIT 構造体の ptr スロットに保存しておけば、将来 xxx() が呼び出されたときにそのメモリを再利用できます。 「13.2.2.1 単純関数の場合の UDF の呼び出し手順」 節 参照 。

関数で戻り値が NULL であることを示すには、is_null1 を設定します。

 
*is_null = 1;

関数がエラーを返すことを示すには、error1 を設定します。

 
*error = 1;

xxx() が、いずれかのレコードの処理で *error1 を設定した場合、カレントレコードおよび XXX() を呼び出したステートメントが処理するそれ以降のレコードについて、関数値が NULL になります(それ以降のレコードでは xxx() の呼び出しも行われません)。注意:MySQL 3.22.10 より古いバージョンでは、*error および *is_null の両方に値を設定する必要があります。

 
*error = 1;
*is_null = 1;


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2.2.5 ユーザ定義関数のコンパイルおよびインストール

UDF を実装するファイルは、サーバが動作するホスト上でコンパイルし、インストールする必要があります。ここでは、MySQL ソースディストリビューションに含まれるサンプル UDF ファイル `udf_example.cc' を使用して、その手順について説明します。このファイルには以下の関数が定義されています。

ダイナミックにロード可能なファイルは、以下のようなコマンドを使用して、共有オブジェクトファイルとしてコンパイルする必要があります。

 
shell> gcc -shared -o udf_example.so myfunc.cc

MySQL ソースツリーの `sql' ディレクトリで以下のコマンドを実行するだけで、システムに合った適切なコンパイラオプションを知ることができます。

 
shell> make udf_example.o

make が表示するコンパイルコマンドをほとんどそのまま使用できます。ただし、行末付近にある -c オプションは指定しないでください。また、行末に -o udf_example.so を追加してください(一部のシステムでは、-c オプションをコマンドに残す必要があります)。

UDF を含む共有オブジェクトをコンパイルしたら、それをインストールして、MySQL に通知する必要があります。`udf_example.cc' をコンパイルして共有オブジェクトを作成すると、`udf_example.so' のような名前のファイルが生成されます(正確な名前はプラットフォームによって異なる可能性があります)。このファイルを `/usr/lib' などのダイナミックリンカ ld の検索対象パスにコピーするか、または共有オブジェクトを置いたディレクトリをリンカ設定ファイル(たとえば `/etc/ld.so.conf')に追加します。

多くのシステムでは、UDF 関数ファイルを置いたディレクトリを参照するように環境変数 LD_LIBRARY または LD_LIBRARY_PATH を設定することもできます。システムで使用する必要がある変数については、dlopen のマニュアルページを参照してください。それらの変数を mysql.server または mysqld_safe の起動スクリプトで設定し、mysqld を再起動する必要があります。

ライブラリをインストールしたら、以下のコマンドを使用して、新しく追加した関数について mysqld に通知します。

 
mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so";
mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
mysql> CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION reverse_lookup
    ->        RETURNS STRING SONAME "udf_example.so";
mysql> CREATE AGGREGATE FUNCTION avgcost
    ->        RETURNS REAL SONAME "udf_example.so";

関数を削除するには、DROP FUNCTION を使用します。

 
mysql> DROP FUNCTION metaphon;
mysql> DROP FUNCTION myfunc_double;
mysql> DROP FUNCTION myfunc_int;
mysql> DROP FUNCTION lookup;
mysql> DROP FUNCTION reverse_lookup;
mysql> DROP FUNCTION avgcost;

CREATE FUNCTION および DROP FUNCTION の各ステートメントは、mysql データベースのシステムテーブル func を更新します。関数の名前、型、および共有ライブラリ名はこのテーブルに保存されます。関数を作成および削除するには、mysql データベースに対する INSERT 特権および DELETE 特権を持っている必要があります。

すでに作成されている関数を CREATE FUNCTION を使用して追加してはいけません。関数を再インストールする必要がある場合は、まず DROP FUNCTION で関数を削除してから CREATE FUNCTION で再インストールします。たとえば新しいバージョンの関数を再コンパイルした場合に、この手順で再インストールして mysqld に新しいバージョンの関数を追加します。再インストールしなければ、サーバは古いバージョンの関数を使い続けます。

mysqld を起動する際に --skip-grant-tables オプションが指定されていなかった場合、アクティブな関数はサーバが起動するたびに再ロードされます。オプションが指定されていた場合、UDF の初期化は行われず、それを使用することはできません(アクティブな関数とは CREATE FUNCTION でロードされたが DROP FUNCTION による削除は行われていない関数)。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.2.3 新しいネイティブ関数の追加

ここでは、新しいネイティブ関数を追加する手順について説明します。注意: この手順には MySQL ソースコードの修正が含まれるので、バイナリディストリビューションにネイティブ関数を追加することはできません。MySQL をソースディストリビューションからコンパイルする必要があります。注意: (新しいバージョンがリリースされたときなど)別のバージョンの MySQL に移行する場合、新しいバージョンでも同じ手順を繰り返してネイティブ関数を追加する必要があります。

MySQL の新しいネイティブ関数を追加するには、以下の手順に従います。

  1. sql_functions[] 配列に関数名を定義する行を `lex.h' に追加する。
  2. 関数のプロトタイプが単純な場合(引数の数が 0、1、2、または 3 個しかない)、lex.h で sql_functions[] 配列の 2 番目の引数として SYM(FUNC_ARG#) (# は引数の数)を指定し、`item_create.cc' に関数オブジェクトを作成する関数を追加する必要がある。この例として、"ABS" および create_funcs_abs() を参照すること。

    関数のプロトタイプが複雑な場合(引数の数が可変の場合など)、`sql_yacc.yy' に 2 行追加する必要がある。まず、yacc が定義する必要があるプリプロセッサシンボルを表す行を追加する(ファイルの先頭部分)。次に、関数のパラメータを定義し、パラメータの指定方法を定義する行を simple_expr 解析規則に追加する。この参考例として、`sql_yacc.yy' の ATAN が出現する部分を参照すること。

  3. `item_func.h' で、関数の戻り値の型(数値または文字列)に応じて、Item_num_func または Item_str_func を継承するクラスを宣言する。
  4. `item_func.cc' で、定義している関数の型(数値型または文字列型)に応じて、以下のいずれかの宣言を追加する。
     
    double   Item_func_newname::val()
    longlong Item_func_newname::val_int()
    String  *Item_func_newname::Str(String *str)
    

    標準のアイテム(Item_num_func など)からオブジェクトを継承する場合、定義する必要があるのはおそらく上記のいずれか 1 つだけであり、残りの関数の定義は親オブジェクトにまかせることができる。たとえば、Item_str_func クラスは ::str() が返す値に対して atof() を実行する val() 関数を定義している。

  5. おそらく、以下のオブジェクト関数も定義する必要がある。
     
    void Item_func_newname::fix_length_and_dec()
    
    この関数は、少なくとも渡された引数に基づいて max_length を計算する必要がある。max_length は、関数が返す可能性がある最大文字数である。関数が NULL 値を返すことができない場合、この関数で maybe_null = 0 も設定する必要がある。関数は、関数の引数が NULL を返すことができるかどうかを、その引数の maybe_null 変数を調べることによって、確認できる。この実装例については、Item_func_mod::fix_length_and_dec を参照すること。

上記の関数はすべてスレッドセーフで作成する必要があります(言い換えると、mutex ロックを使用せずに関数でグローバルまたはスタティックな変数を使用してはいけません)。

NULL を返す場合、::val()::val_int()、または ::str() で、null_value を 1 に設定して、0 を返す必要があります。

::str() オブジェクト関数の場合、さらにいくつかの注意事項があります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.3 MySQL への新しいプロシージャの追加

MySQL では、クライアントに送信する前にクエリのデータにアクセスして、変更できるプロシージャを、C++ を使用して定義できます。データの変更は、1 行ごと、または GROUP BY レベルで実行できます。

MySQL バージョン 3.23 には、プロシージャで実行できる処理を示すサンプルプロシージャが用意されています。

また、mylua を参照することをおすすめします。これは、LUA 言語を使用して実行時に mysqld にプロシージャをロードするツールです。

13.3.1 プロシージャの分析   
13.3.2 プロシージャの作成   


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.3.1 プロシージャの分析

analyse([max elements,[max memory]])

このプロシージャは、`sql/sql_analyse.cc' で定義されています。クエリから返された結果を調べて、その分析結果を返します。

 
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max elements,[max memory]])


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top ] [Contents ] [Index] [ ? ]

13.3.2 プロシージャの作成

現在、プロシージャの作成に関する唯一のドキュメントはソースです。

以下のファイルを参照することで、プロシージャに関するすべての情報を入手できます。


[ << ] [ >> ]           [Top ] [Contents ] [Index] [ ? ]

2004年 10月 9日 texi2html にて作成