第 24 章 MySQL の拡張

目次

24.1 MySQL の内部仕様
24.1.1 MySQL のスレッド
24.1.2 MySQL テストスイート
24.2 MySQL プラグイン API
24.2.1 プラグイン API の特徴
24.2.2 プラグイン API のコンポーネント
24.2.3 プラグインのタイプ
24.2.4 プラグインの作成
24.2.5 プラグインのための MySQL サービス
24.3 MySQL への新しい関数の追加
24.3.1 ユーザー定義関数インタフェースの機能
24.3.2 新しいユーザー定義関数の追加
24.3.3 新しいネイティブ関数の追加
24.4 MySQL のデバッグおよび移植
24.4.1 MySQL サーバーのデバッグ
24.4.2 MySQL クライアントのデバッグ
24.4.3 DBUG パッケージ

24.1 MySQL の内部仕様

この章では、MySQL コードを記述するときに知っておく必要がある多くの事柄について説明します。MySQL 開発の動向を調べたり貢献したりするには、セクション2.9.3「開発ソースツリーを使用して MySQL をインストールする」の手順に従ってください。MySQL の内部仕様について興味がある場合、弊社の internals メーリングリストに登録することもお勧めします。このリストのトラフィックは比較的少なめです。登録する方法については、セクション1.6.1「MySQL メーリングリスト」を参照してください。オラクルの多くの MySQL 開発者が internals リストに登録されており、弊社は MySQL コードを記述するユーザーを支援しています。コードについて質問したり、MySQL プロジェクトに貢献すると思われるパッチを送信したりするときに、このリストを気軽に利用してください。

24.1.1 MySQL のスレッド

MySQL サーバーは次のスレッドを作成します。

  • 接続マネージャースレッドは、サーバーが待機しているネットワークインタフェース上でクライアントの接続要求を処理します。どのプラットフォームでも、1 つのマネージャースレッドが TCP/IP 接続要求を処理します。Unix では、このマネージャースレッドは Unix ソケットファイルの接続要求も処理します。Windows では、1 つのマネージャースレッドが共有メモリーの接続要求を処理し、別のマネージャースレッドが名前付きパイプの接続要求を処理します。サーバーは、待機していないインタフェースを処理するためのスレッドを作成しません。たとえば、Windows サーバーで名前付きパイプ接続のサポートが有効になっていない場合、これらの接続を処理するスレッドは作成されません。

  • 接続マネージャースレッドは、各クライアント接続を、その接続の認証および要求を処理する専用スレッドに関連付けます。マネージャースレッドは、必要に応じて新しいスレッドを作成しますが、まずスレッドキャッシュを調べて接続に使用できるスレッドが含まれているかどうかを確認することによって、それを回避することを試みます。接続が終了すると、スレッドキャッシュが満杯でない場合は、そのスレッドがスレッドキャッシュに返されます。

    スレッドのリソースを制御するパラメータの調整については、セクション8.11.5.1「MySQL のクライアント接続のためのスレッドの使用方法」を参照してください。

  • マスターレプリケーションサーバーでは、スレーブサーバーからの接続はクライアント接続のように扱われ、接続されているスレーブごとに 1 つのスレッドがあります。

  • スレーブレプリケーションサーバー上では、マスターサーバーに接続してそこから更新を読み取るための I/O スレッドが開始されます。マスターから読み取られた更新を適用するための SQL スレッドが開始されます。これらの 2 つのスレッドは別個に実行され、別個に開始および停止できます。

  • シグナルスレッドはすべてのシグナルを処理します。通常、このスレッドは、アラームの処理、および長時間アイドル状態になっている接続をタイムアウトさせる process_alarm() の呼び出しも行います。

  • InnoDB が使用される場合は、デフォルトでは追加の読み取りスレッドおよび書き込みスレッドが存在します。これらの数は、innodb_read_io_threads および innodb_write_io_threads パラメータによって制御されます。セクション14.12「InnoDB の起動オプションおよびシステム変数」を参照してください。

  • mysqld-DUSE_ALARM_THREAD を指定してコンパイルされている場合は、アラームを処理する専用のスレッドが作成されます。これが使用されるのは、sigwait() に問題があるシステムの場合、および専用のシグナル処理スレッドを使用せずにアプリケーションで thr_alarm() コードを使用する場合のみです。

  • サーバーが --flush_time=val オプションを指定して起動された場合、val 秒ごとにすべてのテーブルをフラッシュする専用のスレッドが作成されます。

  • INSERT DELAYED ステートメントが発行された各テーブルには、独自のスレッドが作成されます。セクション13.2.5.2「INSERT DELAYED 構文」を参照してください。

  • イベントスケジューラがアクティブである場合は、スケジューラの 1 つスレッド、および現在実行されている各イベントのそれぞれのスレッドが存在します。セクション20.4.1「イベントスケジューラの概要」を参照してください。

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

24.1.2 MySQL テストスイート

Unix ソースおよびバイナリ配布に付属するテストシステムを使用することによって、ユーザーおよび開発者は MySQL コードのリグレッションテストを実行できます。これらのテストは Unix 上で実行できます。

独自のテストケースを記述することもできます。システム要件を含む MySQL テストフレームワークについては、http://dev.mysql.com/doc/mysqltest/2.0/en/ から入手できるマニュアルを参照してください。

現在のテストケースのセットは MySQL のすべてをテストするわけではありませんが、SQL 処理コードのほとんどの明らかなバグやオペレーティングシステムまたはライブラリの問題を捕捉し、レプリケーションのテストに関してはかなり徹底しています。テストによってコードの 100% がカバーされることを目標としています。弊社はテストスイートへの貢献を歓迎しています。使用しているシステムで重要な機能を検査するテストに貢献すれば、使用しているアプリケーションが将来のすべての MySQL リリースで動作することが保証されます。

テストシステムは、テスト言語インタプリタ (mysqltest)、すべてのテストを実行する Perl スクリプト (mysql-test-run.pl)、特別なテスト言語で記述された実際のテストケース、およびそれらの予期される結果で構成されています。ビルド後にシステム上でテストスイートを実行するには、ソースのルートディレクトリから make test と入力するか、場所を mysql-test ディレクトリに変更して、./mysql-test-run.pl と入力します。バイナリ配布をインストールした場合は、インストールルートディレクトリの下にある mysql-test ディレクトリ (/usr/local/mysql/mysql-test など) に場所を変更して、./mysql-test-run.pl を実行します。すべてのテストが成功します。成功しないものがあれば原因を調べ、MySQL 内のバグを示す場合は問題を報告してください。セクション1.7「質問またはバグをレポートする方法」を参照してください。

1 つのテストに失敗する場合は、--force オプションを指定して mysql-test-run.pl を実行し、ほかのテストも失敗するかどうかを確認してください。

テストスイートを実行するマシン上で mysqld のコピーが実行中である場合、それがポート 9306 または 9307 を使用していなければ、停止する必要はありません。これらのいずれかのポートが使用中である場合、環境変数 MTR_BUILD_THREAD を適切な値に設定します。こうすることで、テストスイートは、マスター、スレーブ、および NDB のポートに別のセットを使用します。例:

shell> export MTR_BUILD_THREAD=31
shell> ./mysql-test-run.pl [options] [test_name]

mysql-test ディレクトリでは、./mysql-test-run.pl test_name を指定することで個々のテストケースを実行できます。

テストスイートについて質問がある場合、または貢献するテストケースがある場合は、MySQL internals メーリングリストに電子メールメッセージを送信してください。セクション1.6.1「MySQL メーリングリスト」 を参照してください。

24.2 MySQL プラグイン API

MySQL は、サーバーコンポーネントの作成を可能にするプラグイン API をサポートします。プラグインはサーバー起動時にロードしたり、実行時にサーバーを再起動せずにロードおよびアンロードしたりできます。API は汎用的なものであり、プラグインが実行できる動作を指定しません。このインタフェースによってサポートされるコンポーネントには、ストレージエンジン、全文パーサープラグイン、サーバー拡張機能、およびその他のコンポーネントがあります。

たとえば、全文パーサープラグインは、組み込みの全文パーサーの代わりに使用したり、強化したりするために使用できます。プラグインは、組み込みパーサーによって使用されるものと異なるルールを使用して、テキストを単語に構文解析できます。これは、組み込みパーサーが予期する特性と異なる特性を持つテキストを構文解析する必要がある場合に役立ちます。

プラグインインタフェースは、以前のユーザー定義関数 (UDF) インタフェースよりも汎用的です。

プラグインインタフェースは mysql データベースの plugin テーブルを使用して、INSTALL PLUGIN ステートメントによって永続的にインストールされているプラグインに関する情報を記録します。このテーブルは、MySQL インストール処理の一部として作成されます。プラグインは、--plugin-load オプションを指定して単一のサーバーを起動することによってもインストールできます。この方法でインストールされたプラグインは、plugin テーブルに記録されません。セクション5.1.8.1「プラグインのインストールおよびアンインストール」を参照してください。

MySQL 5.6 では、サーバープラグイン用の API のほかにクライアントプラグイン用の API もサポートされています。これは、たとえば、サーバー側プラグインとクライアント側プラグインが連係して、クライアントがサーバーにさまざまな認証方式を使用して接続できるようにする認証プラグインによって使用されます。

追加のリソース

Sergei Golubchik および Andrew Hutchings 共著の『MySQL 5.1 Plugin Development』には、プラグイン API に関する豊富な情報が記載されています。この書籍の表題には MySQL Server 5.1 と記載されていますが、書籍内の大半の情報は以降のバージョンにも同様に当てはまります。

24.2.1 プラグイン API の特徴

サーバープラグイン API には次の特徴があります。

  • すべてのプラグインにはいくつかの共通点があります。

    各プラグインには、SQL ステートメント内で参照可能な名前のほかに、ほかの情報を提供する作成者および説明などのメタデータがあります。この情報は、INFORMATION_SCHEMA.PLUGINS テーブルまたは SHOW PLUGINS ステートメントを使用して調査できます。

  • プラグインフレームワークは、別の種類のプラグインに対応するために拡張可能です。

    プラグイン API の一部の特性はすべてのタイプのプラグインに共通していますが、API ではタイプ固有のインタフェース要素も使用できるため、異なるタイプのプラグインを作成できます。ある用途を持つプラグインは、それ自体の要件にもっとも適したインタフェースを持っているため、ほかのプラグインタイプの要件には適していない可能性があります。

    ストレージエンジン、全文パーサー、INFORMATION_SCHEMA テーブルなどのさまざまなタイプのプラグイン用のインタフェースが存在します。ほかのインタフェースも追加できます。

  • プラグインは情報をユーザーに公開します。

    プラグインは、SHOW VARIABLES および SHOW STATUS ステートメントを使用して得られるシステム変数およびステータス変数を実装できます。

  • プラグイン API にはバージョン情報が含まれています。

    プラグイン API に含まれているバージョン情報によって、プラグインライブラリおよびそれに含まれる各プラグインが、ライブラリのビルドに使用された API バージョンを自己認識できます。API がその後変更されるとバージョン番号が変更されますが、サーバーは対象となるプラグインライブラリのバージョン情報を検査して、そのライブラリ内のプラグインをサポートしているかどうかを判別できます。

    バージョン番号には 2 つのタイプがあります。1 つ目は、全般的なプラグインフレームワーク自体のバージョン番号です。各プラグインライブラリには、この種類のバージョン番号が含まれています。2 つ目のタイプのバージョンは個々のプラグインに適用されます。特定のタイプの各プラグインにはそのインタフェースのバージョンがあるため、ライブラリ内の各プラグインにはタイプ固有のバージョン番号があります。たとえば、全文パーサープラグインを含むライブラリには全般的なプラグイン API バージョン番号があり、そのプラグインには全文プラグインインタフェースに固有のバージョン番号があります。

  • プラグイン API はセキュリティー制約を実装します。

    プラグインライブラリは特定の専用ディレクトリにインストールする必要があり、ディレクトリの場所はサーバーによって制御され、実行時に変更することはできません。また、ライブラリには、それがプラグインライブラリであることを識別する特定のシンボルが含まれている必要があります。サーバーは、プラグインとしてビルドされていないものをプラグインとしてロードしません。

  • プラグインはサーバーサービスにアクセスできます。

    サービスインタフェースは、プラグインが通常の関数呼び出しを使用してアクセスできるサーバー機能を公開します。詳細は、セクション24.2.5「プラグインのための MySQL サービス」を参照してください。

サーバープラグイン API は、それに置き換わる前のユーザー定義関数 (UDF) API といくつかの点で似ていますが、古いインタフェースよりさまざまな点で優れています。たとえば、UDF にはバージョン情報がありません。また、新しいプラグインインタフェースには、以前の UDF インタフェースでのセキュリティー問題がありません。プラグインでない UDF を記述するための以前のインタフェースでは、システムのダイナミックリンカーによって検索されたすべてのディレクトリからライブラリをロードでき、UDF ライブラリを識別するシンボルは比較的特定性の低いものでした。

クライアントプラグイン API には類似したアーキテクチャー上の特徴がありますが、クライアントプラグインはサーバープラグインのようにサーバーに直接アクセスしません。

24.2.2 プラグイン API のコンポーネント

サーバープラグインの実装は、いくつかのコンポーネントで構成されています。

SQL ステートメント:

  • INSTALL PLUGIN はプラグインを mysql.plugin テーブルに登録し、プラグインコードをロードします。

  • UNINSTALL PLUGIN はプラグインを mysql.plugin テーブルから登録解除し、プラグインコードをアンロードします。

  • 全文インデックス作成用の WITH PARSER 句は、全文パーサープラグインを特定の FULLTEXT インデックスに関連付けます。

  • SHOW PLUGINS は、サーバープラグインについての情報を表示します。

コマンド行オプションおよびシステム変数:

  • --plugin-load オプションを指定すると、サーバー起動時にプラグインをロードできます。

  • plugin_dir システム変数は、すべてのプラグインをインストールする必要があるディレクトリの場所を指定します。この変数の値は、サーバー起動時に --plugin_dir=path オプションで指定できます。mysql_config --plugindir を指定すると、デフォルトのプラグインディレクトリのパス名が表示されます。

プラグインのロードについての追加情報は、セクション5.1.8.1「プラグインのインストールおよびアンインストール」を参照してください。

プラグインに関連するテーブル:

  • INFORMATION_SCHEMA.PLUGINS テーブルにはプラグイン情報が格納されています。

  • mysql.plugin テーブルには、INSTALL PLUGIN によってインストールされた各プラグインが示され、プラグインを使用するために必要となります。新規に MySQL をインストールする場合、このテーブルはインストール処理中に作成されます。

クライアントプラグインの実装はより単純です。

  • mysql_options() C API 関数の場合は、MYSQL_DEFAULT_AUTH オプションおよび MYSQL_PLUGIN_DIR オプションを指定すると、クライアントプログラムが認証プラグインをロードできます。

  • クライアントプラグインを管理できる C API 関数があります。

MySQL がプラグインを実装する方法を調べるには、MySQL ソース配布内の次のソースファイルを参照してください。

  • include/mysql ディレクトリの plugin.h は、パブリックなプラグイン API を公開しています。プラグインライブラリを記述するすべてのユーザーは、このファイルを調査することをお勧めします。plugin_xxx.h ファイルには特定のタイプのプラグインに関する追加情報があります。client_plugin.h にはクライアントプラグインに固有の情報が含まれています。

  • sql ディレクトリ内の sql_plugin.h および sql_plugin.cc は、内部プラグインの実装を構成しています。sql_acl.cc はサーバーが認証プラグインを使用する場所です。プラグイン開発者はこれらのファイルを参照する必要はありません。サーバーがプラグインを処理する方法について知りたい場合は、これらのファイルを参照できます。

  • sql-common ディレクトリ内の、client_plugin.h は C API クライアントプラグイン関数を実装し、client.c はクライアント認証サポートを実装します。プラグイン開発者はこれらのファイルを参照する必要はありません。サーバーがプラグインを処理する方法について知りたい場合は、これらのファイルを参照できます。

24.2.3 プラグインのタイプ

プラグイン API を使用すると、さまざまな機能が実装されたプラグインを作成できます。

  • ストレージエンジン

  • 全文パーサー

  • デーモン

  • INFORMATION_SCHEMA テーブル

  • 準同期レプリケーション

  • 監査

  • 認証

次のセクションでは、これらのプラグインタイプの概要について説明します。

24.2.3.1 ストレージエンジンプラグイン

MySQL サーバーによって使用されるプラガブルストレージエンジンアーキテクチャーにより、ストレージエンジンをプラグインとして作成し、実行中のサーバーにロードしたりそのサーバーからアンロードしたりできます。このアーキテクチャーの説明については、セクション15.11「MySQL ストレージエンジンアーキテクチャーの概要」を参照してください。

プラグイン API を使用してストレージエンジンを作成するための方法については、「MySQL Internals: Writing a Custom Storage Engine」を参照してください。

24.2.3.2 全文パーサープラグイン

MySQL には、全文操作 (インデックス付けされるテキストの構文解析、または検索に使用される用語を判別するためのクエリー文字列の構文解析) のためにデフォルトで使用される組み込みパーサーがあります。全文処理の場合、構文解析とは、単語を構成する文字シーケンスや単語の境界となる位置を定義するルールに基づいて、テキストまたはクエリー文字列から単語を抽出することを意味します。

インデックスを作成するために構文解析するとき、パーサーは各単語をサーバーに渡し、サーバーは単語を全文インデックスに追加します。クエリー文字列を構文解析するとき、パーサーは各単語をサーバーに渡し、サーバーは単語を蓄積して検索に使用します。

組み込みの全文パーサーの構文解析プロパティーは、セクション12.9「全文検索関数」に記載されています。これらのプロパティーには、テキストから単語を抽出する方法を決定するためのルールも含まれています。パーサーは、短い単語または長い単語を除外する ft_min_word_lenft_max_word_len などの特定のシステム変数、および無視される一般的な単語を示すストップワードリストの影響を受けます。

プラグイン API を使用すると、独自の全文パーサーを作成して、パーサーの基本的な機能を制御できます。パーサープラグインは 2 つの役割のいずれかで動作できます。

  • プラグインを組み込みパーサーの代わりに使用できます。この役割では、プラグインは構文解析する対象の入力データを読み取り、入力データを単語に分割し、単語を (インデックス設定または単語の蓄積のために) サーバーに渡します。

    この方法でパーサーを使用する 1 つの理由は、入力データを単語に分割する方法を決定するために、組み込みパーサーのルールとは異なるルールを使用する必要がある場合です。たとえば、組み込みパーサーはcase-sensitiveというテキストをcaseおよびsensitiveという 2 つの単語で構成されるものとして解釈するが、あるアプリケーションではこのテキストを単一の単語として扱う必要があることもあります。

  • プラグインは組み込みパーサーのフロントエンドとして動作することによって、組み込みパーサーと連動できます。この役割では、プラグインは入力データからテキストを抽出してテキストをパーサーに渡し、パーサーはその通常の構文解析ルールを使用してテキストを単語に分割します。この構文解析は、特に、ft_xxx システム変数およびストップワードリストの影響を受けます。

    この方法でパーサーを使用する 1 つの理由は、PDF ドキュメント、XML ドキュメント、.doc ファイルなどのコンテンツにインデックスを設定する必要がある場合です。組み込みパーサーはこれらのタイプの入力データのためのものではありませんが、プラグインを使用してこれらの入力ソースからテキストを抜き出し、テキストを組み込みパーサーに渡すことができます。

パーサープラグインが両方の役割で動作することも可能です。つまり、平文でないテキスト入力からテキストを抽出し (フロントエンドの役割)、テキストを構文解析して単語を得る (組み込みパーサーの代わりとなる) ことができます。

全文プラグインは、インデックスごとに全文インデックスに関連付けられます。つまり、パーサープラグインを最初にインストールした状態では、全文操作にパーサープラグインを使用することはできません。単にパーサープラグインが利用可能になるだけです。たとえば、個々の FULLTEXT インデックスを作成するときに、全文パーサープラグインを WITH PARSER 句に指定できるようになります。テーブル作成時にそのようなインデックスを作成するには、次のステートメントを実行します。

CREATE TABLE t
( doc CHAR(255), FULLTEXT INDEX (doc) WITH PARSER my_parser
) ENGINE=MyISAM;

または、テーブルが作成されたあとにインデックスを追加できます。

ALTER TABLE t ADD FULLTEXT INDEX (doc) WITH PARSER my_parser;

パーサーをインデックスに関連付けるために SQL を変更する箇所は、WITH PARSER 句のみです。検索は以前と同様に指定し、クエリーを変更する必要はありません。

パーサープラグインを FULLTEXT インデックスに関連付けると、そのインデックスを使用するためにこのプラグインが必要になります。そのパーサープラグインが削除された場合は、パーサープラグインに関連付けられていたすべてのインデックスが使用できなくなります。プラグインを使用できないテーブルを使用しようとするとエラーになりますが、DROP TABLE は引き続き実行できます。

全文プラグインについては、セクション24.2.4.4「全文パーサープラグインの作成」を参照してください。MySQL 5.6 では、MyISAM を使用したフルインデックスプラグインのみがサポートされます。

24.2.3.3 デーモンプラグイン

デーモンプラグインは、サーバーによって実行されるがサーバーと通信しないコードに使用される単純なタイプのプラグインです。MySQL 配布には、定期的にハートビートメッセージをファイルに書き込むデーモンプラグインの例が含まれています。

デーモンプラグインについては、セクション24.2.4.5「デーモンプラグインの作成」を参照してください。

24.2.3.4 INFORMATION_SCHEMA プラグイン

INFORMATION_SCHEMA プラグインを使用すると、INFORMATION_SCHEMA データベース経由でユーザーに公開されるサーバーメタデータを含むテーブルを作成できます。たとえば、InnoDB は、INFORMATION_SCHEMA プラグインを使用して、現在のトランザクションおよびロックに関する情報が含まれているテーブルを提供しています。

INFORMATION_SCHEMA プラグインについては、セクション24.2.4.6「INFORMATION_SCHEMA プラグインの作成」を参照してください。

24.2.3.5 準同期レプリケーションプラグイン

MySQL レプリケーションはデフォルトでは非同期です。準同期レプリケーションでは、マスター側で実行されたコミットは、トランザクションを実行したセッションに戻る前に、少なくとも 1 つのスレーブがトランザクションのイベントを受け取ってログに記録したことを通知するまでブロックされます。準同期レプリケーションは、補完的なマスタープラグインおよびクライアントプラグインを介して実装されます。セクション17.3.8「準同期レプリケーション」を参照してください。

準同期レプリケーションについては、セクション24.2.4.7「準同期レプリケーションプラグインの作成」を参照してください。

24.2.3.6 監査プラグイン

MySQL 5.6 では、サーバー操作に関する情報を関係者に報告できるプラガブルな監査インタフェースがサーバーによって提供されます。現時点では、次の操作について監査通知が行われます (インタフェースは汎用的なものですが、サーバーを変更してほかの情報を報告できます)。

  • 一般クエリーログへのメッセージの書き込み (ログが有効にされている場合)

  • エラーログへのメッセージの書き込み

  • クライアントへのクエリー結果の送信

監査プラグインは、サーバー操作についての通知を受け取るために監査インタフェースに登録できます。監査可能なイベントがサーバー内で発生すると、サーバーは通知が必要であるかどうかを判別します。登録されている各監査プラグインについて、サーバーはプラグインが対象としているイベントクラスに対してイベントを照合し、一致する場合はイベントをプラグインに渡します。

このインタフェースでは、監査プラグインが重要だとみなすイベントクラスの操作の通知のみを監査プラグインが受け取り、ほかのものを無視できます。このインタフェースは、操作をイベントクラスまで分類し、さらに各クラス内のイベントサブクラスまで分割します。

監査プラグインに監査可能なイベントが通知されると、監査プラグインは現在の THD 構造体へのポインタ、およびイベントについての情報を含む構造へのポインタを受け取ります。プラグインはイベントを検査し、適切な監査アクションを実行します。たとえば、プラグインは、結果セットを生成したステートメント、ログに記録されたステートメント、結果の行数、操作についての現在のユーザー、または失敗した操作のエラーコードを確認できます。

監査プラグインについては、セクション24.2.4.8「監査プラグインの作成」を参照してください。

24.2.3.7 認証プラグイン

MySQL 5.6 はプラガブルな認証をサポートしています。認証プラグインはサーバー側とクライアント側の両方に存在します。サーバー側のプラグインは、クライアントがサーバーに接続するときにクライアントによって使用される認証方式を実装します。クライアント側のプラグインはサーバー側のプラグインと通信して、サーバー側のプラグインが必要とする認証情報を提供します。クライアント側のプラグインは、ユーザーと対話し、サーバーに送信するパスワードやその他の認証情報の要求などのタスクを実行することもあります。セクション6.3.7「プラガブル認証」を参照してください。

プラガブルな認証では、あるユーザーが別のユーザーの ID を取得するプロキシユーザー機能も使用できます。サーバー側の認証プラグインは、接続するユーザーが使用するべき ID の所有者であるユーザーの名前をサーバーに返すことができます。セクション6.3.9「プロキシユーザー」を参照してください。

認証プラグインについては、セクション24.2.4.9「認証プラグインの作成」を参照してください。

24.2.3.8 パスワード検証プラグイン

MySQL 5.6.6 以降では、サーバーはパスワードをテストするプラグインを記述するためのインタフェースを提供しています。そのようなプラグインは、2 つの機能を実装します。

  • パスワードを割り当てるステートメント (CREATE USERGRANTSET PASSWORD ステートメントなど) の非常に弱いパスワード、および PASSWORD() 関数および OLD_PASSWORD() 関数に引数として指定されたパスワードの拒否。

  • VALIDATE_PASSWORD_STRENGTH() SQL 関数でのパスワード候補の強さの評価。

このタイプのプラグインを作成については、セクション24.2.4.10「パスワード検証プラグインの作成」を参照してください。

24.2.4 プラグインの作成

プラグインライブラリを作成するには、ライブラリファイルに含まれるプラグインを示す必要なディスクリプタ情報を提供し、各プラグインのインタフェース関数を記述する必要があります。

すべてのサーバープラグインには、プラグイン API に情報を提供する一般ディスクリプタ、および特定のタイプのプラグインにプラグインインタフェースについての情報を提供するタイプ固有のディスクリプタがある必要があります。一般ディスクリプタの構造体は、すべてのプラグインタイプで同じです。タイプ固有のディスクリプタの構造体はプラグインタイプによって異なり、プラグインが実行する必要がある動作の要件によって決定されます。サーバープラグインインタフェースを使用すると、プラグインはステータス変数およびシステム変数を公開することもできます。これらの変数は、SHOW STATUS ステートメントや SHOW VARIABLES ステートメント、および対応する INFORMATION_SCHEMA テーブルを介して表示できます。

クライアント側のプラグインの場合、アーキテクチャーは少し異なります。各プラグインにはディスクリプタがある必要がありますが、一般ディスクリプタとタイプ固有のディスクリプタに分割されていません。そうではなく、ディスクリプタはすべてのクライアントプラグインに共通する固定された一連のメンバーで始まり、この共通メンバーのあとに、特定のプラグインタイプを実装するために必要な追加のメンバーが続きます。

プラグインは、C または C++ (あるいは C の呼び出し規則を使用できる別の言語) で記述できます。プラグインは動的にロードおよびアンロードされるため、オペレーティングシステムが動的ロードをサポートしている必要があり、呼び出し元アプリケーションを (静的にではなく) 動的にコンパイルしている必要があります。サーバープラグインの場合、これは mysqld を動的にコンパイルする必要があることを意味します。

サーバープラグインには実行中のサーバーの一部となるコードが含まれるため、プラグインを記述するときは、サーバーコードを作成する場合に該当するすべての制約に従う必要があります。たとえば、libstdc++ ライブラリの関数を使用しようとすると、問題が生じることがあります。これらの制約はサーバーの今後のバージョンで変更される場合があるため、サーバーのアップグレードにより、古いサーバー用に作成されたプラグインの改訂が必要になることがあります。これらの制約については、セクション2.9.4「MySQL ソース構成オプション」およびセクション2.9.5「MySQL のコンパイルに関する問題」を参照してください。

どのようなアプリケーションがプラグインを使用するかはわからないため、クライアントプラグインの作成者は、呼び出し元アプリケーションが持つシンボルに依存しないようにしてください。

24.2.4.1 プラグインの作成の概要

次の手順では、プラグインライブラリの作成に必要なステップの概要を示します。以降のセクションでは、プラグインのデータ構造体の設定、および特定のタイプのプラグインの作成について詳しく説明します。

  1. プラグインのソースファイルに、プラグインライブラリが必要とするヘッダーファイルをインクルードします。plugin.h ファイルは必須であり、ライブラリではほかのファイルも必要になることがあります。例:

    #include <stdlib.h>
    #include <ctype.h>
    #include <mysql/plugin.h>
  2. プラグインライブラリファイル用のディスクリプタ情報をセットアップします。サーバープラグインの場合は、ライブラリディスクリプタを記述します。ライブラリディスクリプタには、ファイル内の各サーバープラグインの一般プラグインディスクリプタが含まれている必要があります。詳細は、セクション24.2.4.2.1「サーバープラグインライブラリおよびプラグインディスクリプタ」を参照してください。また、ライブラリ内の各サーバープラグインのタイプ固有のディスクリプタをセットアップします。各プラグインの一般ディスクリプタは、タイプ固有のディスクリプタを指しています。

    クライアントプラグインの場合は、クライアントディスクリプタを記述します。詳細は、セクション24.2.4.2.3「クライアントプラグインディスクリプタ」を参照してください。

  3. 各プラグインのプラグインインタフェース関数を作成します。たとえば、各プラグインの一般プラグインディスクリプタは、サーバーがプラグインをロードおよびアンロードするときに呼び出す初期化関数および初期化解除関数を指しています。プラグインのタイプ固有のディスクリプタは、インタフェース関数を指していることもあります。

  4. サーバープラグインの場合は、ステータス変数およびシステム変数を設定します (ある場合)。

  5. プラグインライブラリを共有ライブラリとしてコンパイルし、プラグインディレクトリにインストールします。詳細は、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」を参照してください。

  6. サーバープラグインの場合は、プラグインをサーバーに登録します。詳細は、セクション5.1.8.1「プラグインのインストールおよびアンインストール」を参照してください。

  7. プラグインをテストして、正しく動作することを確認します。

24.2.4.2 プラグインのデータ構造体

プラグインライブラリファイルには、それに格納されているプラグインを示すディスクリプタ情報が含まれています。

プラグインライブラリにサーバープラグインが含まれている場合、プラグインライブラリには次のディスクリプタ情報が含まれている必要があります。

  • ライブラリディスクリプタは、ライブラリによって使用される一般的なサーバープラグイン API バージョン番号を示し、ライブラリ内の各サーバープラグインの一般プラグインディスクリプタを含んでいます。このディスクリプタのフレームワークを提供するには、plugin.h ヘッダーファイルから 2 つのマクロを呼び出します。

    mysql_declare_plugin(name) ... one or more server plugin descriptors here ...mysql_declare_plugin_end;

    マクロが展開され、API バージョンの宣言が自動的に提供されます。プラグインディスクリプタを指定する必要があります。

  • ライブラリディスクリプタ内の各一般的なサーバープラグインは、st_mysql_plugin 構造体によって記述されます。このプラグインのディスクリプタ構造体には、すべてのタイプのサーバープラグインに共通する情報 (プラグインタイプを示す値、プラグイン名、作成者、説明、ライセンスタイプ、サーバーがプラグインをロードおよびアンロードするときに呼び出す初期化関数と初期化解除関数へのポインタ、およびプラグインが実装するステータス変数またはシステム変数へのポインタ) が含まれています。

  • ライブラリディスクリプタ内の各一般的なサーバープラグインディスクリプタには、タイプ固有のプラグインディスクリプタへのポインタも含まれています。プラグインはタイプごとに独自の API を持つことがあるため、タイプ固有のディスクリプタの構造体はプラグインタイプによって異なります。タイプ固有のプラグインディスクリプタには、タイプ固有の API バージョン番号およびそのプラグインタイプを実装するために必要な関数へのポインタが含まれています。たとえば、全文パーサープラグインには、初期化関数と初期化解除関数、およびメインの構文解析関数があります。サーバーはプラグインを使用してテキストを構文解析するときに、これらの関数を呼び出します。

プラグインライブラリには、ライブラリ内の各プラグインの一般ディスクリプタおよびタイプ固有のディスクリプタによって参照されるインタフェース関数も格納されています。

プラグインライブラリにクライアントプラグインが格納されている場合は、そのプラグインのディスクリプタが含まれている必要があります。そのディスクリプタはすべてのクライアントプラグインに共通する固定された一連のメンバーで始まり、そのあとにプラグインタイプ固有のメンバーが続きます。ディスクリプタフレームワークを提供するには、client_plugin.h ヘッダーファイルから 2 つのマクロを呼び出します。

mysql_declare_client_plugin(plugin_type) ... members common to all client plugins ... ... type-specific extra members ...
mysql_end_client_plugin;

プラグインライブラリには、クライアントディスクリプタによって参照されるインタフェース関数も格納されています。

mysql_declare_plugin() マクロおよび mysql_declare_client_plugin() マクロは呼び出す方法が若干異なり、これはプラグインライブラリの内容が関係しています。次のガイドラインはこのルールを要約しています。

  • mysql_declare_plugin() および mysql_declare_client_plugin() は同じソースファイル内で使用できます。これは、1 つのプラグインライブラリにサーバープラグインとクライアントプラグインを両方格納できることを意味します。ただし、mysql_declare_plugin()mysql_declare_client_plugin() はそれぞれ 1 回しか使用できません。

  • mysql_declare_plugin() は複数のサーバープラグイン宣言が可能であるため、1 つのプラグインライブラリに複数のサーバープラグインを格納できます。

  • mysql_declare_client_plugin() は、単一のクライアントプラグイン宣言のみを行うことができます。複数のクライアントプラグインを作成するには、別々のプラグインライブラリを使用する必要があります。

プラグインライブラリに存在して libmysqlclient に組み込まれていないクライアントプラグインをクライアントプログラムが探す場合、クライアントプログラムはプラグイン名と同じベース名を持つファイルを探します。たとえば、ライブラリのサフィクスとして .so を使用するシステムで、auth_xxx という名前のクライアント認証プラグインをプログラムで使用する必要がある場合、プログラムは auth_xxx.so という名前のファイルを探します。(OS X では、プログラムはまず auth_xxx.dylib を探し、次に auth_xxx.so を探します。)このため、プラグインライブラリにクライアントプラグインが格納されている場合、ライブラリはそのプラグインと同じベース名である必要があります。

サーバープラグインを格納しているライブラリの場合は異なります。--plugin-load オプションおよび INSTALL PLUGIN ステートメントはライブラリファイル名を明示的に指定するため、ライブラリ名とライブラリに格納されているサーバープラグインの名前に明示的な関係がある必要はありません。

24.2.4.2.1 サーバープラグインライブラリおよびプラグインディスクリプタ

サーバープラグインを格納するすべてのプラグインライブラリには、ファイル内の各サーバープラグインの一般プラグインディスクリプタが含まれているライブラリディスクリプタが格納されている必要があります。このセクションでは、サーバープラグイン用のライブラリおよび一般ディスクリプタを記述する方法について説明します。

ライブラリディスクリプタは 2 つのシンボルを定義する必要があります。

  • _mysql_plugin_interface_version_ は全般的なプラグインフレームワークのバージョン番号を指定します。これは MYSQL_PLUGIN_INTERFACE_VERSION シンボルを使用して指定し、このシンボルは plugin.h ファイルに定義します。

  • _mysql_plugin_declarations_ は、プラグイン宣言の配列を定義し、すべてのメンバーが 0 に設定された宣言で終わります。各宣言は、st_mysql_plugin 構造体のインスタンスです (plugin.h 内でも定義されます)。ライブラリ内のサーバープラグインごとに、これらのいずれかが必要となります。

サーバーがライブラリ内でこれらの 2 つのシンボルを見つけることができない場合、サーバーはこれを正当なプラグインライブラリとして受け入れず、拒否してエラーを発生させます。これにより、ライブラリをプラグインライブラリとして特別にビルドしないかぎり、プラグイン用にライブラリを使用できなくなります。

必須の 2 つのシンボルを定義する通常の方法は、plugin.h ファイルから mysql_declare_plugin() マクロおよび mysql_declare_plugin_end マクロを使用することです。

mysql_declare_plugin(name) ... one or more server plugin descriptors here ...mysql_declare_plugin_end;

各サーバープラグインには、サーバープラグイン API に情報を提供する一般ディスクリプタが必要となります。一般ディスクリプタの構造体はすべてのプラグインタイプで同じです。plugin.h ファイル内の st_mysql_plugin 構造体によって、このディスクリプタが定義されます。

struct st_mysql_plugin
{ int type; void *info; const char *name; const char *author; const char *descr; int license; int (*init)(void *); int (*deinit)(void *); unsigned int version; struct st_mysql_show_var *status_vars; struct st_mysql_sys_var **system_vars; void * __reserved1; unsigned long flags;
};

st_mysql_plugin ディスクリプタ構造体のメンバーは次のように使用します。char * メンバーは、NULL で終わる文字列として指定してください。

  • type: プラグインの型。これは plugin.h のプラグインタイプ値のいずれかである必要があります。

    #define MYSQL_UDF_PLUGIN 0
    #define MYSQL_STORAGE_ENGINE_PLUGIN 1
    #define MYSQL_FTPARSER_PLUGIN 2
    #define MYSQL_DAEMON_PLUGIN 3
    #define MYSQL_INFORMATION_SCHEMA_PLUGIN 4
    #define MYSQL_AUDIT_PLUGIN 5
    #define MYSQL_REPLICATION_PLUGIN 6
    #define MYSQL_AUTHENTICATION_PLUGIN 7
    ...

    たとえば、全文パーサープラグインの場合、type 値は MYSQL_FTPARSER_PLUGIN です。

  • info: プラグインのタイプ固有のディスクリプタへのポインタ。このディスクリプタの構造体は、一般プラグインディスクリプタ構造体とは異なり、プラグインの特定のタイプによって異なります。バージョンを制御するために、すべてのプラグインタイプのタイプ固有のディスクリプタの最初のメンバーは、タイプのインタフェースバージョンであることが予期されています。これにより、サーバーはタイプに関係なくすべてのプラグインのタイプ固有のバージョンをチェックできます。ディスクリプタには、バージョン番号のあとに、必要なほかのメンバー (サーバーがプラグインを適切に呼び出すために必要となるコールバック関数やその他の情報など) が含まれています。特定タイプのサーバープラグインの記述に関する以降のセクションでは、それらのタイプ固有のディスクリプタの構造体について説明しています。

  • name: プラグイン名を指定する文字列。これは mysql.plugin テーブルに表示される名前であり、この名前を使用して、SQL ステートメント (INSTALL PLUGINUNINSTALL PLUGIN など) でプラグインを参照したり、--plugin-load オプションで指定してプラグインを参照したりします。この名前は、INFORMATION_SCHEMA.PLUGINS テーブルおよび SHOW PLUGINS の出力にも表示されます。

    プラグイン名は、サーバーオプション名で始まらないようにしてください。そのようにした場合、サーバーはプラグインの初期化に失敗します。たとえば、サーバーには --socket オプションがあるため、socketsocket_plugin などのプラグイン名を使用しないでください。

  • author: プラグイン作成者を示す文字列。これには任意の文字列を指定できます。

  • desc: プラグインの概要を説明する文字列。これには任意の文字列を指定できます。

  • license: プラグインのライセンスタイプ。値には PLUGIN_LICENSE_PROPRIETARYPLUGIN_LICENSE_GPL、または PLUGIN_LICENSE_BSD のいずれかを指定できます。

  • init: 1 回だけ実行される初期化関数であり、そのような関数がない場合は NULL です。サーバーはプラグインをロードするときにこの関数を実行し、これは INSTALL PLUGIN で実行されるか、mysql.plugin テーブルにリストされているプラグインの場合はサーバー起動時に実行されます。この関数は、プラグインを識別するために使用される内部構造体を指している 1 つの引数を受け取ります。成功した場合はゼロ、および失敗した場合はゼロ以外を返します。

  • deinit: 1 回だけ実行される初期化解除関数であり、そのような関数がない場合は NULL です。サーバーはプラグインをアンロードするときにこの関数を実行し、これは UNINSTALL PLUGIN で実行されるか、mysql.plugin テーブルにリストされているプラグインの場合はサーバーのシャットダウン時に実行されます。この関数は、プラグインを識別するために使用される内部構造体を指している 1 つの引数を受け取ります。成功した場合はゼロ、および失敗した場合はゼロ以外を返します。

  • version: プラグインのバージョン番号。プラグインがインストールされている場合は、この値を INFORMATION_SCHEMA.PLUGINS テーブルから取得できます。この値にはメジャー番号とマイナー番号が含まれています。16 進数の定数として値を記述する場合、形式は 0xMMNN であり、ここで MM および NN はそれぞれメジャー番号とマイナー番号です。たとえば、0x0302 はバージョン 3.2 を表します。

  • status_vars: プラグインに関連付けられているステータス変数の構造体へのポインタであり、そのような変数がない場合は NULL です。プラグインをインストールすると、これらの変数は SHOW STATUS ステートメントの出力として表示されます。

    status_vars のメンバーは、NULL ではない場合、ステータス変数を記述する st_mysql_show_var 構造体の配列を指しています。セクション24.2.4.2.2「サーバープラグインのステータス変数およびシステム変数」を参照してください。

  • system_vars: プラグインに関連付けられているシステム変数の構造体へのポインタであり、そのような変数がない場合は NULL です。これらのオプションおよびシステム変数は、プラグイン内の変数を初期化するために使用できます。

    system_vars のメンバーは、NULL ではない場合、システム変数を記述する st_mysql_sys_var 構造体の配列を指しています。セクション24.2.4.2.2「サーバープラグインのステータス変数およびシステム変数」を参照してください。

  • __reserved1: 今後利用するためのプレースホルダ。現時点では NULL を設定してください。

  • flags: プラグインフラグ。個々のビットは各種のフラグに対応しています。この値には、該当するフラグの論理和を設定してください。次のフラグを使用できます。

    #define PLUGIN_OPT_NO_INSTALL 1UL
    #define PLUGIN_OPT_NO_UNINSTALL 2UL 

    PLUGIN_OPT_NO_INSTALL は、INSTALL PLUGIN ステートメントを使用してプラグインを実行時にロードできないことを示します。これは、--plugin-load オプションを使用してサーバー起動時にロードする必要があるプラグインの場合に適しています。PLUGIN_OPT_NO_UNINSTALL は、UNINSTALL PLUGIN ステートメントを使用してプラグインを実行時にアンロードできないことを示します。

    このメンバーは MySQL 5.6.3 で追加されました。

サーバーは、プラグインをロードおよびアンロードする場合にのみ、一般プラグインディスクリプタ内の init 関数および deinit 関数を呼び出します。これらは、SQL ステートメントによってプラグインが起動された場合などのプラグインの使用時には関係がありません。

たとえば、simple_parser という名前の単一の全文パーサープラグインを含むライブラリのディスクリプタ情報は、次のようになります。

mysql_declare_plugin(ftexample)
{ MYSQL_FTPARSER_PLUGIN, &simple_parser_descriptor, "simple_parser", "Oracle Corporation", "Simple Full-Text Parser", PLUGIN_LICENSE_GPL, simple_parser_plugin_init, simple_parser_plugin_deinit, 0x0001, simple_status, simple_system_variables, NULL, 0
}
mysql_declare_plugin_end;

全文パーサープラグインの場合、タイプは MYSQL_FTPARSER_PLUGIN である必要があります。これは、FULLTEXT インデックスの作成時に WITH PARSER 句で使用する場合に、プラグインが正当なものであることを識別する値です。(ほかのプラグインタイプは、この句に対して正当ではありません。)

plugin.h では、mysql_declare_plugin() マクロおよび mysql_declare_plugin_end マクロを次のように定義します。

#ifndef MYSQL_DYNAMIC_PLUGIN
#define __MYSQL_DECLARE_PLUGIN(NAME, VERSION, PSIZE, DECLS) \
MYSQL_PLUGIN_EXPORT int VERSION= MYSQL_PLUGIN_INTERFACE_VERSION; \
MYSQL_PLUGIN_EXPORT int PSIZE= sizeof(struct st_mysql_plugin); \
MYSQL_PLUGIN_EXPORT struct st_mysql_plugin DECLS[]= {
#else
#define __MYSQL_DECLARE_PLUGIN(NAME, VERSION, PSIZE, DECLS) \
MYSQL_PLUGIN_EXPORT int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION; \
MYSQL_PLUGIN_EXPORT int _mysql_sizeof_struct_st_plugin_= sizeof(struct st_mysql_plugin); \
MYSQL_PLUGIN_EXPORT struct st_mysql_plugin _mysql_plugin_declarations_[]= {
#endif
#define mysql_declare_plugin(NAME) \
__MYSQL_DECLARE_PLUGIN(NAME, \ builtin_ ## NAME ## _plugin_interface_version, \ builtin_ ## NAME ## _sizeof_struct_st_plugin, \ builtin_ ## NAME ## _plugin)
#define mysql_declare_plugin_end ,{0,0,0,0,0,0,0,0,0,0,0,0,0}}
注記

これらの宣言では、MYSQL_DYNAMIC_PLUGIN シンボルを定義した場合にのみ、_mysql_plugin_interface_version_ シンボルを定義します。これは、プラグインを共有ライブラリとしてビルドするためのコンパイルコマンドの一部として -DMYSQL_DYNAMIC_PLUGIN を指定する必要があることを意味します。

上記のようにマクロが使用された場合は、次のコードが展開され、必要なシンボル (_mysql_plugin_interface_version_ および _mysql_plugin_declarations_) が定義されます。

int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION;
int _mysql_sizeof_struct_st_plugin_= sizeof(struct st_mysql_plugin);
struct st_mysql_plugin _mysql_plugin_declarations_[]= {
{ MYSQL_FTPARSER_PLUGIN, &simple_parser_descriptor, "simple_parser", "Oracle Corporation", "Simple Full-Text Parser", PLUGIN_LICENSE_GPL, simple_parser_plugin_init, simple_parser_plugin_deinit, 0x0001, simple_status, simple_system_variables, NULL, 0
} ,{0,0,0,0,0,0,0,0,0,0,0,0}}
};

前の例では一般ディスクリプタ内に単一のプラグインを宣言しましたが、複数のプラグインを宣言することもできます。mysql_declare_plugin()mysql_declare_plugin_end の間に宣言をカンマで区切って順番にリストします。

MySQL サーバープラグインは、C または C++ (あるいは C の呼び出し規則を使用できる別の言語) で記述できます。C++ プラグインを記述する場合に使用するべきではない C++ 機能は、グローバル構造体を初期化するための非定数変数です。st_mysql_plugin 構造などの構造体のメンバーは、定数変数でのみ初期化してください。前に示した simple_parser ディスクリプタはこの要件を満たすため、C++ プラグインで許容されます。

mysql_declare_plugin(ftexample)
{ MYSQL_FTPARSER_PLUGIN, &simple_parser_descriptor, "simple_parser", "Oracle Corporation", "Simple Full-Text Parser", PLUGIN_LICENSE_GPL, simple_parser_plugin_init, simple_parser_plugin_deinit, 0x0001, simple_status, simple_system_variables, NULL, 0
}
mysql_declare_plugin_end;

一般ディスクリプタを記述するための別の有効な方法を次に示します。ここでは、プラグイン名、作成者、および説明を指定するために定数変数が使用されています。

const char *simple_parser_name = "simple_parser";
const char *simple_parser_author = "Oracle Corporation";
const char *simple_parser_description = "Simple Full-Text Parser";
mysql_declare_plugin(ftexample)
{ MYSQL_FTPARSER_PLUGIN, &simple_parser_descriptor, simple_parser_name, simple_parser_author, simple_parser_description, PLUGIN_LICENSE_GPL, simple_parser_plugin_init, simple_parser_plugin_deinit, 0x0001, simple_status, simple_system_variables, NULL, 0
}
mysql_declare_plugin_end;

ただし、次の一般ディスクリプタは無効です。ここではプラグイン名、作成者、および説明を指定するために構造体メンバーが使用されていますが、C++ では構造は定数イニシャライザと見なされません。

typedef struct
{ const char *name; const char *author; const char *description;
} plugin_info;
plugin_info parser_info = { "simple_parser", "Oracle Corporation", "Simple Full-Text Parser"
};
mysql_declare_plugin(ftexample)
{ MYSQL_FTPARSER_PLUGIN, &simple_parser_descriptor, parser_info.name, parser_info.author, parser_info.description, PLUGIN_LICENSE_GPL, simple_parser_plugin_init, simple_parser_plugin_deinit, 0x0001, simple_status, simple_system_variables, NULL, 0
}
mysql_declare_plugin_end;
24.2.4.2.2 サーバープラグインのステータス変数およびシステム変数

サーバープラグインインタフェースを使用すると、プラグインが一般プラグインディスクリプタの status_vars メンバーおよび system_vars メンバーを使用して、ステータス変数およびシステム変数を公開できます。

一般プラグインディスクリプタの status_vars メンバーは、0 でない場合、st_mysql_show_var 構造体の配列を指しており、各構造には 1 つのステータス変数が記述されていて、すべてのメンバーが 0 に設定された構造があとに続きます。st_mysql_show_var 構造体の定義は次のとおりです。

struct st_mysql_show_var { const char *name; char *value; enum enum_mysql_show_type type;
};

プラグインがインストールされると、プラグイン名と name 値がアンダースコアで結合されて、SHOW STATUS によって表示される名前が形成されます。

次の表は、許可されるステータス変数の type 値、および対応する変数を示しています。

表 24.1 サーバープラグインのステータス変数のタイプ

変数型意味
SHOW_BOOLブール変数へのポインタ
SHOW_INT整数変数へのポインタ
SHOW_LONGlong 整数変数へのポインタ
SHOW_LONGLONGlonglong 整数変数へのポインタ
SHOW_CHAR文字列
SHOW_CHAR_PTR文字列へのポインタ
SHOW_ARRAY別の st_mysql_show_var 配列へのポインタ
SHOW_FUNC関数へのポインタ
SHOW_DOUBLEdouble へのポインタ

SHOW_FUNC タイプの場合、関数が呼び出されると関数は out パラメータに値を設定し、表示される変数に関する情報がこのパラメータによって提供されます。関数のシグネチャーは次のようになります。

#define SHOW_VAR_FUNC_BUFF_SIZE 1024
typedef int (*mysql_show_var_func) (void *thd, struct st_mysql_show_var *out, char *buf);

system_vars メンバーは、0 でない場合、st_mysql_sys_var 構造体の配列を指しており、各構造には 1 つのシステム変数が記述されていて (これはコマンド行または構成ファイルからも設定できます)、すべてのメンバーが 0 に設定された構造があとに続きます。st_mysql_sys_var 構造体は次のように定義します。

struct st_mysql_sys_var { int flags; const char *name, *comment; int (*check)(THD*, struct st_mysql_sys_var *, void*, st_mysql_value*); void (*update)(THD*, struct st_mysql_sys_var *, void*, const void*);
};

フラグによって、追加フィールドが必要に応じて付加されます。

利便性のため、プラグイン内の新しいシステム変数の作成をより簡単にするための多数のマクロが定義されています。

マクロでは次のフィールドを使用できます。

  • name: システム変数の引用符で囲まれていない識別子。

  • varname: 静的変数の識別子。識別子がない場合は name フィールドと同じです。

  • opt: システム変数に追加で使用されるフラグ。次の表は、許容されるフラグを示しています。

    表 24.2 サーバープラグインのシステム変数フラグ

    フラグ値説明
    PLUGIN_VAR_READONLYシステム変数は読み取り専用である
    PLUGIN_VAR_NOSYSVARシステム変数は実行時にユーザーに表示されない
    PLUGIN_VAR_NOCMDOPTシステム変数はコマンド行から構成できない
    PLUGIN_VAR_NOCMDARGコマンド行に引数は不要である (通常はブール変数に使用します)
    PLUGIN_VAR_RQCMDARGコマンド行に引数が必要である (これはデフォルトです)
    PLUGIN_VAR_OPCMDARGコマンド行の引数はオプションである
    PLUGIN_VAR_MEMALLOC文字列変数に使用され、文字列の格納にメモリーが割り当てられることを示す

  • comment: サーバーヘルプメッセージに表示される説明コメント。この変数が非表示の場合は NULL です。

  • check: チェック関数。デフォルトは NULL です。

  • update: 更新関数。デフォルトは NULL です。

  • default: 変数のデフォルト値。

  • minimum: 変数の最小値。

  • maximum: 変数の最大値。

  • blocksize: 変数のブロックサイズ。値が設定されると、もっとも近い blocksize の倍数に丸められます。

システム変数には、静的変数を使用して直接アクセスするか、SYSVAR() アクセス機能マクロを使用してアクセスできます。完全性を期すために、SYSVAR() マクロが提供されています。通常、これはコードがベースとなる変数に直接アクセスできない場合にのみ使用してください。

例:

static int my_foo;
static MYSQL_SYSVAR_INT(foo_var, my_foo, PLUGIN_VAR_RQCMDARG, "foo comment", NULL, NULL, 0, 0, INT_MAX, 0); ... SYSVAR(foo_var)= value; value= SYSVAR(foo_var); my_foo= value; value= my_foo;

セッション変数には THDVAR() アクセス機能マクロを介してのみアクセスできます。例:

static MYSQL_THDVAR_BOOL(some_flag, PLUGIN_VAR_NOCMDARG, "flag comment", NULL, NULL, FALSE); ... if (THDVAR(thd, some_flag)) { do_something(); THDVAR(thd, some_flag)= FALSE; }

すべてのグローバルシステム変数およびセッションシステム変数は、使用する前に mysqld に公開する必要があります。これは、NULL で終わる変数の配列を作成し、プラグインパブリックインタフェースでそれにリンクすることによって行うことができます。例:

static struct st_mysql_sys_var *my_plugin_vars[]= { MYSQL_SYSVAR(foo_var), MYSQL_SYSVAR(some_flag), NULL
};
mysql_declare_plugin(fooplug)
{ MYSQL_..._PLUGIN, &plugin_data, "fooplug", "foo author", "This does foo!", PLUGIN_LICENSE_GPL, foo_init, foo_fini, 0x0001, NULL, my_plugin_vars, NULL, 0
}
mysql_declare_plugin_end;

次の支援マクロを使用すると、さまざまなタイプのシステム変数を宣言できます。

  • 1 バイトのブール値 (0 = FALSE、1 = TRUE) である my_bool 型のブールシステム変数。

    MYSQL_THDVAR_BOOL(name, opt, comment, check, update, default)
    MYSQL_SYSVAR_BOOL(name, varname, opt, comment, check, update, default)
  • NULL で終わる文字列へのポインタである char* 型の文字列システム変数。

    MYSQL_THDVAR_STR(name, opt, comment, check, update, default)
    MYSQL_SYSVAR_STR(name, varname, opt, comment, check, update, default)
  • 各種の整数システム変数。

    • 通常は 4 バイトの符号付きワードである int システム変数。

      MYSQL_THDVAR_INT(name, opt, comment, check, update, default, min, max, blk)
      MYSQL_SYSVAR_INT(name, varname, opt, comment, check, update, default, minimum, maximum, blocksize)
    • 通常は 4 バイトの符号なしワードである unsigned int システム変数。

      MYSQL_THDVAR_UINT(name, opt, comment, check, update, default, min, max, blk)
      MYSQL_SYSVAR_UINT(name, varname, opt, comment, check, update, default, minimum, maximum, blocksize)
    • 通常は 4 バイトまたは 8 バイトの符号付きワードである long システム変数。

      MYSQL_THDVAR_LONG(name, opt, comment, check, update, default, min, max, blk)
      MYSQL_SYSVAR_LONG(name, varname, opt, comment, check, update, default, minimum, maximum, blocksize)
    • 通常は 4 バイトまたは 8 バイトの符号なしワードである unsigned long システム変数。

      MYSQL_THDVAR_ULONG(name, opt, comment, check, update, default, min, max, blk)
      MYSQL_SYSVAR_ULONG(name, varname, opt, comment, check, update, default, minimum, maximum, blocksize)
    • 通常は 8 バイトの符号付きワードである long long システム変数。

      MYSQL_THDVAR_LONGLONG(name, opt, comment, check, update, default, minimum, maximum, blocksize)
      MYSQL_SYSVAR_LONGLONG(name, varname, opt, comment, check, update, default, minimum, maximum, blocksize)
    • 通常は 8 バイトの符号なしワードである unsigned long long システム変数。

      MYSQL_THDVAR_ULONGLONG(name, opt, comment, check, update, default, minimum, maximum, blocksize)
      MYSQL_SYSVAR_ULONGLONG(name, varname, opt, comment, check, update, default, minimum, maximum, blocksize)
    • 通常は 4 バイトまたは 8 バイトの符号なしワードである unsigned long システム変数。設定可能な値の範囲は typelib 内の要素の数の序数であり、0 から始まります。

      MYSQL_THDVAR_ENUM(name, opt, comment, check, update, default, typelib)
      MYSQL_SYSVAR_ENUM(name, varname, opt, comment, check, update, default, typelib)
    • 通常は 8 バイトの符号なしワードである unsigned long long システム変数。各ビットは typelib 内の要素を表します。

      MYSQL_THDVAR_SET(name, opt, comment, check, update, default, typelib)
      MYSQL_SYSVAR_SET(name, varname, opt, comment, check, update, default, typelib)

内部的には、変更されることがあるすべてのプラグインシステム変数は HASH 構造体に格納されます。

サーバーのコマンド行でのヘルプテキストの表示は、コマンド行オプションに関係するすべての変数の DYNAMIC_ARRAY をコンパイルしてそれらをソートし、それを繰り返して各オプションを表示することによって処理されます。

コマンド行オプションが処理されると、handle_option() 関数 (my_getopt.c) によって argv から削除され、実質的にその役割が終わります。

サーバーはプラグインのインストール処理中 (プラグインが正常にロードされた直後、かつプラグインの初期化関数が呼び出される前) にコマンド行オプションを処理します。

実行時にロードされるプラグインは構成オプションを利用できないため、使用可能なデフォルト値がある必要があります。プラグインをインストールすると、mysqld の初期化時にロードされ、構成オプションをコマンド行または my.cnf 内に設定できます。

プラグインで thd パラメータを読み取り専用にすることを検討してください。

24.2.4.2.3 クライアントプラグインディスクリプタ

各クライアントプラグインには、クライアントプラグイン API に情報を提供するディスクリプタが必要となります。ディスクリプタの構造体は、すべてのクライアントプラグインに共通する固定された一連のメンバーで始まり、プラグインタイプ固有のメンバーがあとに続きます。

client_plugin.h ファイル内の st_mysql_client_plugin 構造体は、共通メンバーが含まれている一般的なディスクリプタを定義します。

struct st_mysql_client_plugin
{ int type; unsigned int interface_version; const char *name; const char *author; const char *desc; unsigned int version[3]; const char *license; void *mysql_api; int (*init)(char *, size_t, int, va_list); int (*deinit)(); int (*options)(const char *option, const void *);
};

st_mysql_client_plugin ディスクリプタ構造体の共通メンバーは、次のように使用します。char * メンバーは、NULL で終わる文字列として指定してください。

  • type: プラグインの型。これは client_plugin.h のプラグインタイプ値 (MYSQL_CLIENT_AUTHENTICATION_PLUGIN など) のいずれかである必要があります。

  • interface_version: プラグインインタフェースのバージョン。たとえば、認証プラグインの場合、これは MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION です。

  • name: プラグイン名を指定する文字列。これは、MYSQL_DEFAULT_AUTH オプションを指定して mysql_options() を呼び出すか、MySQL クライアントプログラムに --default-auth オプションを指定するときにプラグインを参照するための名前です。

  • author: プラグイン作成者を示す文字列。これには任意の文字列を指定できます。

  • desc: プラグインの概要を説明する文字列。これには任意の文字列を指定できます。

  • version: メジャー、マイナー、およびその下のバージョンを示す 3 つの整数の配列であるプラグインバージョン。たとえば、{1,2,3} はバージョン 1.2.3 を示します。

  • license: ライセンスタイプを指定する文字列。

  • mysql_api: 内部で使用されます。プラグインディスクリプタでは NULL に指定します。

  • init: 1 回だけ実行される初期化関数であり、そのような関数がない場合は NULL です。クライアントライブラリはプラグインをロードするときにこの関数を実行します。関数は、成功した場合はゼロ、および失敗した場合はゼロ以外を返します。

    エラーが発生した場合、init 関数は最初の 2 つの引数を使用してエラーメッセージを返します。最初の引数は char バッファーへのポインタであり、2 番目の引数はバッファー長を示します。init 関数によって返されるすべてのメッセージは NULL で終わる必要があるため、最大のメッセージ長はバッファー長から 1 を引いた長さです。それに続く引数が mysql_load_plugin() に渡されます。先頭の引数は、以降に存在する引数の数を示し (ない場合は 0)、そのあとに残りの引数が続きます。

  • deinit: 1 回だけ実行される初期化解除関数であり、そのような関数がない場合は NULL です。クライアントライブラリは、プラグインをアンロードするときにこの関数を実行します。この関数は引数を受け取りません。成功した場合はゼロ、および失敗した場合はゼロ以外を返します。

  • options: プラグインに渡されるオプションを処理するための関数であり、そのような関数がない場合は NULL です。この関数は、オプション名およびその値へのポインタを表す 2 つの引数を受け取ります。関数は、成功した場合はゼロ、および失敗した場合はゼロ以外を返します。

特定のクライアントプラグインタイプでは、共通のディスクリプタメンバーのあとに、そのタイプのプラグインを実装するために必要な追加メンバーが続くことがあります。たとえば、認証プラグインの st_mysql_client_plugin_AUTHENTICATION 構造体には、認証を実行するためにクライアントライブラリが呼び出す関数が最後にあります。

プラグインを宣言するには、mysql_declare_client_plugin() マクロおよび mysql_end_client_plugin マクロを使用します。

mysql_declare_client_plugin(plugin_type) ... members common to all client plugins ... ... type-specific extra members ...
mysql_end_client_plugin;

type または interface_version メンバーを明示的に指定しないでください。mysql_declare_client_plugin() マクロは plugin_type 引数を使用して、これらの値を自動的に生成します。たとえば、認証クライアントプラグインは次のように宣言します。

mysql_declare_client_plugin(AUTHENTICATION) "my_auth_plugin", "Author Name", "My Client Authentication Plugin", {1,0,0}, "GPL", NULL, my_auth_init, my_auth_deinit, my_auth_options, my_auth_main
mysql_end_client_plugin;

この宣言では、AUTHENTICATION 引数を使用して、type および interface_version メンバーに MYSQL_CLIENT_AUTHENTICATION_PLUGIN および MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION を設定しています。

プラグインタイプによっては、ディスクリプタの共通メンバーに続いてほかのメンバーがあることがあります。たとえば、認証プラグインの場合、サーバーとの通信を処理する関数 (上記のディスクリプタでは my_auth_main()) があります。セクション24.2.4.9「認証プラグインの作成」を参照してください。

通常、認証プラグインの使用をサポートしているクライアントプログラムは、mysql_options() を呼び出して MYSQL_DEFAULT_AUTH オプションおよび MYSQL_PLUGIN_DIR オプションを設定することによって、プラグインをロードします。

char *plugin_dir = "path_to_plugin_dir";
char *default_auth = "plugin_name";
mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);
mysql_options(&mysql, MYSQL_DEFAULT_AUTH, default_auth);

一般にプログラムは、ユーザーがデフォルト値をオーバーライドできるようにする --plugin-dir および --default-auth オプションも受け付けます。

クライアントプログラムで低レベルのプラグイン管理が必要である場合は、クライアントライブラリに st_mysql_client_plugin 引数を受け取る関数を含めます。セクション23.8.14「C API クライアントプラグイン関数」を参照してください。

24.2.4.3 プラグインライブラリのコンパイルおよびインストール

プラグインを記述したら、プラグインをコンパイルしてインストールする必要があります。共有オブジェクトをコンパイルする手順はシステムによって異なります。CMake を使用してライブラリをビルドする場合は、それがシステムの正しいコンパイルコマンドを生成できる必要があります。ライブラリに somepluglib という名前を指定した場合、共有オブジェクトファイルの名前は somepluglib.so のようになります。(使用しているシステムではファイル名のサフィクスが異なる場合もあります。)

CMake を使用する場合は、構成ファイルをセットアップして、プラグインがコンパイルおよびインストールされるようにする必要があります。MySQL ソース配布の plugin ディレクトリの下にあるプラグインの例をガイドとして使用してください。

次のような CMakeLists.txt を作成します。

MYSQL_ADD_PLUGIN(somepluglib somepluglib.c MODULE_ONLY MODULE_OUTPUT_NAME "somepluglib")

CMakeMakefile を生成するとき、コンパイルコマンドに -DMYSQL_DYNAMIC_PLUGIN フラグを渡し、リンカーに -lmysqlservices フラグを渡します。これは、プラグインサービスインタフェースを介して提供されるサービスの関数でリンクするために必要となります。セクション24.2.5「プラグインのための MySQL サービス」を参照してください。

CMake を実行してから make を実行します。

shell> cmake .shell> make

CMake の構成オプションを指定する必要がある場合、リストについてはセクション2.9.4「MySQL ソース構成オプション」を参照してください。たとえば、プラグインのインストール先となる MySQL のベースディレクトリを指定するために CMAKE_INSTALL_PREFIX を指定する場合があります。このオプションに使用する値は SHOW VARIABLES で確認できます。

mysql> SHOW VARIABLES LIKE 'basedir';+---------------+------------------+
| Variable_name | Value |
+---------------+------------------+
| base | /usr/local/mysql |
+---------------+------------------+

ライブラリをインストールするプラグインディレクトリの場所は、plugin_dir システム変数によって指定されます。例:

mysql> SHOW VARIABLES LIKE 'plugin_dir';+---------------+-----------------------------------+
| Variable_name | Value |
+---------------+-----------------------------------+
| plugin_dir | /usr/local/mysql/lib/mysql/plugin |
+---------------+-----------------------------------+

プラグインライブラリをインストールするには、make を使用します。

shell> make install

make install によってプラグインライブラリが適切なディレクトリにインストールされたことを確認します。インストール後、サーバーが実行するためのアクセス権がライブラリに設定されていることを確認してください。

24.2.4.4 全文パーサープラグインの作成

MySQL 5.6 では、MyISAM でのみ全文パーサープラグインがサポートされます。全文パーサープラグインについての概要は、セクション24.2.3.2「全文パーサープラグイン」を参照してください。

全文パーサーサーバープラグインは、組み込みの全文パーサーの代わりにするか、それを変更するために使用できます。このセクションでは、simple_parser という名前の全文パーサープラグインを記述する方法について説明します。このプラグインは、MySQL の組み込み全文パーサーによって使用されるものよりも簡単なルールに基づいて構文解析を実行し、単語は空白文字を含まない文字列です。

この手順では、MySQL ソース配布の plugin/fulltext ディレクトリ内のソースコードを使用しているため、このディレクトリに場所を変更してください。次の手順では、プラグインライブラリを作成する方法について説明します。

  1. 全文パーサープラグインを記述するために、プラグインソースファイルで次のヘッダーファイルをインクルードします。プラグインの機能および要件によっては、ほかの MySQL のヘッダーファイルまたは一般的なヘッダーファイルが必要になることもあります。

    #include <mysql/plugin.h>

    plugin.h は、MYSQL_FTPARSER_PLUGIN サーバープラグインタイプ、およびプラグインを宣言するために必要なデータ構造体を定義します。

  2. プラグインライブラリファイルのライブラリディスクリプタをセットアップします。

    このディスクリプタには、サーバープラグインの一般プラグインディスクリプタが含まれています。全文パーサープラグインの場合、タイプは MYSQL_FTPARSER_PLUGIN である必要があります。これは、FULLTEXT インデックスの作成時に WITH PARSER 句で使用する場合に、プラグインが正当なものであることを識別する値です。(ほかのプラグインタイプは、この句に対して正当ではありません。)

    たとえば、simple_parser という名前の単一の全文パーサープラグインを格納するライブラリのライブラリディスクリプタは、次のようになります。

    mysql_declare_plugin(ftexample)
    { MYSQL_FTPARSER_PLUGIN, &simple_parser_descriptor, "simple_parser", "Oracle Corporation", "Simple Full-Text Parser", PLUGIN_LICENSE_GPL, simple_parser_plugin_init, simple_parser_plugin_deinit, 0x0001, simple_status, simple_system_variables, NULL, 0
    }
    mysql_declare_plugin_end;

    name メンバー (simple_parser) は、INSTALL PLUGINUNINSTALL PLUGIN などのステートメントでプラグインを参照するために使用する名前を示しています。これは、SHOW PLUGINS または INFORMATION_SCHEMA.PLUGINS によって表示される名前でもあります。

    詳細は、セクション24.2.4.2.1「サーバープラグインライブラリおよびプラグインディスクリプタ」を参照してください。

  3. タイプ固有のプラグインディスクリプタをセットアップします。

    ライブラリディスクリプタの各一般プラグインディスクリプタは、タイプ固有のディスクリプタを指しています。全文パーサープラグインの場合、タイプ固有のディスクリプタは plugin.h ファイル内の st_mysql_ftparser 構造体のインスタンスです。

    struct st_mysql_ftparser
    { int interface_version; int (*parse)(MYSQL_FTPARSER_PARAM *param); int (*init)(MYSQL_FTPARSER_PARAM *param); int (*deinit)(MYSQL_FTPARSER_PARAM *param);
    };

    構造体定義に示されているように、ディスクリプタにはインタフェースバージョン番号があり、3 つの関数へのポインタが含まれています。

    インタフェースのバージョン番号はシンボルを使用して指定し、MYSQL_xxx_INTERFACE_VERSION という形式です。全文パーサープラグインの場合、シンボルはMYSQL_FTPARSER_INTERFACE_VERSIONです。ソースコードには、include/mysql/plugin_ftparser.h に定義されている全文パーサープラグインの実際のインタフェースバージョン番号があります。

    init メンバーおよび deinit メンバーは、関数を指すようにするか、関数が不要な場合は 0 に設定します。parse メンバーは、構文解析を実行する関数を指している必要があります。

    simple_parser 宣言では、そのディスクリプタは &simple_parser_descriptor によって示されています。ディスクリプタは、全文プラグインインタフェースのバージョン番号 (MYSQL_FTPARSER_INTERFACE_VERSION によって指定されます)、プラグインの構文解析関数、初期化関数、および初期化解除関数を指定します。

    static struct st_mysql_ftparser simple_parser_descriptor=
    { MYSQL_FTPARSER_INTERFACE_VERSION, simple_parser_parse, simple_parser_init, simple_parser_deinit
    };

    全文パーサーサーバープラグインは、インデックス設定と検索の 2 つの異なるコンテキストで使用されます。両方のコンテキストで、サーバーはプラグインを呼び出した各 SQL ステートメントの処理の開始時と終了時に初期化関数および初期化解除関数を呼び出します。ただし、ステートメントの処理中に、サーバーはコンテキストに固有の方法でメインの構文解析関数を呼び出します。

    • インデックス設定の場合、サーバーはインデックス設定される各カラム値に対してパーサーを呼び出します。

    • 検索の場合、サーバーは検索文字列を構文解析するためにパーサーを呼び出します。パーサーはステートメントによって処理される行に対して呼び出されることもあります。自然言語モードでは、サーバーがパーサーを呼び出す必要はありません。クエリー拡張を使用したブールモードフレーズ検索または自然言語検索の場合、インデックスにない情報についてカラム値を構文解析するためにパーサーが使用されます。また、FULLTEXT インデックスのないカラムに対してブールモード検索が実行された場合は、組み込みパーサーが呼び出されます。(プラグインは特定のインデックスに関連付けられます。インデックスがない場合、プラグインは使用されません。)

    一般プラグインディスクリプタのプラグイン宣言には、初期化関数および初期化解除関数を指している init メンバーおよび deinit メンバーがあり、指している先のタイプ固有のプラグインディスクリプタにもこれらがあります。ただし、これらの関数のペアは目的が異なり、異なる理由で呼び出されます。

    • 一般プラグインディスクリプタのプラグイン宣言の場合、初期化関数および初期化解除関数は、プラグインがロードおよびアンロードされるときに呼び出されます。

    • タイプ固有のプラグインディスクリプタの場合、初期化関数および初期化解除関数は、プラグインが使用される SQL ステートメントごとに呼び出されます。

    プラグインディスクリプタに指定されている各インタフェース関数は、成功の場合はゼロ、および失敗の場合はゼロ以外を返し、構文解析するコンテキストが含まれている MYSQL_FTPARSER_PARAM 構造体を指している引数を受け取ります。この構造体の定義は次のとおりです。

    typedef struct st_mysql_ftparser_param
    { int (*mysql_parse)(struct st_mysql_ftparser_param *, char *doc, int doc_len); int (*mysql_add_word)(struct st_mysql_ftparser_param *, char *word, int word_len, MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info); void *ftparser_state; void *mysql_ftparam; struct charset_info_st *cs; char *doc; int length; int flags; enum enum_ftparser_mode mode;
    } MYSQL_FTPARSER_PARAM;

    構造体メンバーは次のように使用されます。

    • mysql_parse: サーバーの組み込みパーサーを呼び出すコールバック関数へのポインタ。プラグインが組み込みパーサーのフロントエンドとして動作する場合は、このコールバックを使用します。つまり、プラグインの構文解析関数が呼び出されたとき、その関数は入力を処理してテキストを抽出し、テキストを mysql_parse コールバックに渡します。

      このコールバック関数の最初のパラメータは、param 値自体となります。

      param->mysql_parse(param, ...);

      フロントエンドプラグインは、テキストを抽出して一度にすべてのテキストを組み込みパーサーに渡すか、テキストを抽出して一度にテキストの一部分ずつを組み込みパーサーに渡すことができます。ただし、この場合、組み込みサーバーはテキストの一部分同士の間に暗黙的な単語の分割があるかのように処理します。

    • mysql_add_word: 全文インデックスまたは検索語のリストに単語を追加するコールバック関数へのポインタ。このコールバックは、組み込みパーサーをパーサープラグインに置き換えた場合に使用します。つまり、プラグインの構文解析関数が呼び出されたとき、その関数が入力を単語に構文解析し、各単語について mysql_add_word コールバックが呼び出されます。

      このコールバック関数の最初のパラメータは、param 値自体となります。

      param->mysql_add_word(param, ...);
    • ftparser_state: これは一般的なポインタです。プラグインは独自の目的のために内部で使用される情報を指すようにこれを設定できます。

    • mysql_ftparam: これはサーバーによって設定されます。これは最初の引数として mysql_parse または mysql_add_word コールバックに渡されます。

    • cs: テキストの文字セットについての情報へのポインタであり、情報がない場合は 0 です。

    • doc: 構文解析されるテキストへのポインタ。

    • length: 構文解析されるテキストのバイト単位の長さ。

    • flags: パーサーのフラグ。特別なフラグがない場合、これはゼロです。現時点で、ゼロ以外のフラグは MYSQL_FTFLAGS_NEED_COPY のみであり、これは mysql_add_word() が単語のコピーを保存する必要がある (つまり、単語は上書きされるバッファー内にあるため、単語へのポインタを使用できない) ことを意味します。このメンバーは MySQL 5.1.12 で追加されました。

      このフラグは、MySQL (パーサープラグインを呼び出す前)、パーサープラグイン自体、または mysql_parse() 関数が設定またはリセットできます

    • mode: 構文解析のモード。この値は次の定数のいずれかです。

      • MYSQL_FTPARSER_SIMPLE_MODE: 単純で高速なモードで構文解析し、インデックス設定および自然言語クエリーに使用されます。パーサーは、インデックスを設定する単語のみをサーバーに渡します。パーサーが長さ制限またはストップワードリストを使用して無視する単語を判別する場合、パーサーはそのような単語をサーバーに渡しません。

      • MYSQL_FTPARSER_WITH_STOPWORDS: ストップワードモードで構文解析します。これはフレーズ照合のためのブール検索で使用されます。パーサーは、ストップワードや通常の長さ制限の範囲外にある単語であっても、すべての単語をサーバーに渡します。

      • MYSQL_FTPARSER_FULL_BOOLEAN_INFO: ブールモードで構文解析します。これはブールクエリー文字列の構文解析に使用されます。パーサーは単語だけでなくブールモード演算子も認識し、mysql_add_word コールバックを使用してこれらをトークンとしてサーバーに渡します。渡されるトークンの種類をサーバーに通知するために、プラグインは MYSQL_FTPARSER_BOOLEAN_INFO 構造体に値を設定して、この構造体へのポインタを渡す必要があります。

    パーサーがブールモードで呼び出された場合、param->mode 値は MYSQL_FTPARSER_FULL_BOOLEAN_INFO になります。サーバーにトークン情報を渡すためにパーサーが使用する MYSQL_FTPARSER_BOOLEAN_INFO 構造体は次のようになります。

    typedef struct st_mysql_ftparser_boolean_info
    { enum enum_ft_token_type type; int yesno; int weight_adjust; char wasign; char trunc; char prev; char *quot;
    } MYSQL_FTPARSER_BOOLEAN_INFO;

    パーサーは構造体メンバーに次のように値を設定します。

    • type: トークンのタイプ。許容されるタイプを次の表に示します。

      表 24.3 全文パーサーのトークンタイプ

      トークン値意味
      FT_TOKEN_EOFデータの終わり
      FT_TOKEN_WORD通常の単語
      FT_TOKEN_LEFT_PARENグループまたは部分式の始まり
      FT_TOKEN_RIGHT_PARENグループまたは部分式の終わり
      FT_TOKEN_STOPWORDストップワード

    • yesno: 一致が発生するために単語が存在する必要があるかどうか。0 の場合、単語はオプションでですが、存在する場合は一致の関連性が増加することを意味します。0 より大きい値は、単語が存在する必要があることを意味します。0 より小さい値は、単語が存在していない必要があることを意味します。

    • weight_adjust: 単語の一致の重要度を決定する重み付けの要素。これは、関連性計算での単語の重要性を増加または減少させるために使用できます。値がゼロの場合は、重み付けによる調整がないことを示します。ゼロより大きい値または小さい値は、重みが大きいことまたは小さいことをそれぞれ意味します。< および > 演算子を使用するセクション12.9.2「ブール全文検索」の例は、重み付けがどのように機能するかを示しています。

    • wasign: 重み付け要素の符号。負の値は ~ ブール検索演算子のように作用し、これによって、関連性に対する単語の貢献度がマイナスになります。

    • trunc: ブールモードの * 切り捨て演算子が指定された場合のように、照合を実行するかどうか。

    プラグインで、MYSQL_FTPARSER_BOOLEAN_INFO 構造体の prev メンバーおよび quot メンバーを使用しないでください。

    注記

    ブール演算子 @ distance は現在のプラグインパーサーフレームワークによってサポートされません。ブール全文検索の演算子については、セクション12.9.2「ブール全文検索」を参照してください。

  4. プラグインインタフェース関数をセットアップします。

    ライブラリディスクリプタ内の一般プラグインディスクリプタは、サーバーがプラグインをロードおよびアンロードするときに呼び出す初期化関数および初期化解除関数を指定します。simple_parser の場合、これらの関数は何も行いませんが、成功したことを示すためにゼロを返します。

    static int simple_parser_plugin_init(void *arg __attribute__((unused)))
    { return(0);
    }
    static int simple_parser_plugin_deinit(void *arg __attribute__((unused)))
    { return(0);
    }

    これらの関数は実際に何も行わないため、これらを省略して、プラグイン宣言でそれぞれに 0 を指定できます。

    simple_parser のタイプ固有のプラグインディスクリプタは、プラグインが使用されるときにサーバーが呼び出す初期化関数、初期化解除関数、および構文解析関数を指定します。simple_parser の場合、初期化関数および初期化解除関数は何も行いません。

    static int simple_parser_init(MYSQL_FTPARSER_PARAM *param __attribute__((unused)))
    { return(0);
    }
    static int simple_parser_deinit(MYSQL_FTPARSER_PARAM *param __attribute__((unused)))
    { return(0);
    }

    ここでも、これらの関数は何も行わないため、これらを省略して、プラグインディスクリプタでそれぞれに 0 を指定できます。

    メインの構文解析関数 simple_parser_parse() は組み込みの全文パーサーの代わりに動作するため、テキストを単語に分割して各単語をサーバーに渡す必要があります。構文解析関数の最初の引数は、構文解析するコンテキストが含まれている構造体へのポインタです。この構造体には、構文解析されるテキストを指している doc メンバー、およびテキストの長さを示す length メンバーがあります。このプラグインによって実行される単純な構文解析では、空白文字を含まない文字列を単語とみなすため、次のように単語を識別します。

    static int simple_parser_parse(MYSQL_FTPARSER_PARAM *param)
    { char *end, *start, *docend= param->doc + param->length; for (end= start= param->doc;; end++) { if (end == docend) { if (end > start) add_word(param, start, end - start); break; } else if (isspace(*end)) { if (end > start) add_word(param, start, end - start); start= end + 1; } } return(0);
    }

    パーサーは単語を検出するごとに、関数 add_word() を呼び出して単語をサーバーに渡します。add_word() はヘルパー関数にすぎず、プラグインインタフェースの一部ではありません。パーサーは、構文解析されるコンテキストへのポインタ、単語へのポインタ、および長さの値を add_word() に渡します。

    static void add_word(MYSQL_FTPARSER_PARAM *param, char *word, size_t len)
    { MYSQL_FTPARSER_BOOLEAN_INFO bool_info= { FT_TOKEN_WORD, 0, 0, 0, 0, ' ', 0 }; param->mysql_add_word(param, word, len, &bool_info);
    }

    ブールモード構文解析の場合、前に st_mysql_ftparser_boolean_info 構造体の説明で示したように、add_word()bool_info 構造体のメンバーに値を設定します。

  5. ステータス変数をセットアップします。simple_parser プラグインの場合、次のステータス変数の配列は、1 つのステータス変数を静的テキストの値で設定し、別のステータス変数を long 整数変数に格納されている値で設定しています。

    long number_of_calls= 0;
    struct st_mysql_show_var simple_status[]=
    { {"static", (char *)"just a static text", SHOW_CHAR}, {"called", (char *)&number_of_calls, SHOW_LONG}, {0,0,0}
    };

    プラグインがインストールされると、プラグイン名と name 値がアンダースコアで結合されて、SHOW STATUS によって表示される名前が形成されます。上記の配列の場合、生成されるステータス変数名は simple_parser_static および simple_parser_called です。この規則は、プラグインの変数をプラグイン名を使用して簡単に表示できることを意味します。

    mysql> SHOW STATUS LIKE 'simple_parser%';+----------------------+--------------------+
    | Variable_name | Value |
    +----------------------+--------------------+
    | simple_parser_static | just a static text |
    | simple_parser_called | 0 |
    +----------------------+--------------------+
  6. プラグインライブラリのオブジェクトファイルをコンパイルおよびインストールするには、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」の手順を使用します。ライブラリファイルを使用するには、ライブラリファイルがプラグインディレクトリ (plugin_dir システム変数で指定されているディレクトリ) にインストールされている必要があります。simple_parser プラグインの場合、ソースから MySQL をビルドするときにコンパイルおよびインストールされます。これはバイナリ配布にも含められます。ビルド処理では、mypluglib.so という名前の共有オブジェクトライブラリが生成されます (サフィクスはプラットフォームによって異なる場合があります)。

  7. プラグインを使用するには、プラグインをサーバーに登録します。たとえば、プラグインを実行時に登録するには次のステートメントを使用します (必要に応じてサフィクスを変更します)。

    mysql> INSTALL PLUGIN simple_parser SONAME 'mypluglib.so';

    プラグインのロードについての追加情報は、セクション5.1.8.1「プラグインのインストールおよびアンインストール」を参照してください。

  8. プラグインのインストールを検証するには、INFORMATION_SCHEMA.PLUGINS テーブルを調査するか、SHOW PLUGINS ステートメントを使用します。

  9. プラグインをテストして、正しく動作することを確認します。

    文字列カラムを含むテーブルを作成し、パーサープラグインをそのカラムの FULLTEXT インデックスに関連付けます。

    mysql> CREATE TABLE t (c VARCHAR(255), ->  FULLTEXT (c) WITH PARSER simple_parser -> ) ENGINE=MyISAM;Query OK, 0 rows affected (0.01 sec)

    このテーブルにいくつかのテキストを挿入して検索を実行します。これらにより、パーサープラグインは空白文字でないすべての文字を単語文字として処理することが確認されます。

    mysql> INSERT INTO t VALUES ->  ('latin1_general_cs is a case-sensitive collation'), ->  ('I\'d like a case of oranges'), ->  ('this is sensitive information'), ->  ('another row'), ->  ('yet another row');Query OK, 5 rows affected (0.02 sec)
    Records: 5 Duplicates: 0 Warnings: 0
    mysql> SELECT c FROM t;+-------------------------------------------------+
    | c |
    +-------------------------------------------------+
    | latin1_general_cs is a case-sensitive collation |
    | I'd like a case of oranges |
    | this is sensitive information |
    | another row |
    | yet another row |
    +-------------------------------------------------+
    5 rows in set (0.00 sec)
    mysql> SELECT MATCH(c) AGAINST('case') FROM t;+--------------------------+
    | MATCH(c) AGAINST('case') |
    +--------------------------+
    | 0 |
    | 1.2968142032623 |
    | 0 |
    | 0 |
    | 0 |
    +--------------------------+
    5 rows in set (0.00 sec)
    mysql> SELECT MATCH(c) AGAINST('sensitive') FROM t;+-------------------------------+
    | MATCH(c) AGAINST('sensitive') |
    +-------------------------------+
    | 0 |
    | 0 |
    | 1.3253291845322 |
    | 0 |
    | 0 |
    +-------------------------------+
    5 rows in set (0.01 sec)
    mysql> SELECT MATCH(c) AGAINST('case-sensitive') FROM t;+------------------------------------+
    | MATCH(c) AGAINST('case-sensitive') |
    +------------------------------------+
    | 1.3109166622162 |
    | 0 |
    | 0 |
    | 0 |
    | 0 |
    +------------------------------------+
    5 rows in set (0.01 sec)
    mysql> SELECT MATCH(c) AGAINST('I\'d') FROM t;+--------------------------+
    | MATCH(c) AGAINST('I\'d') |
    +--------------------------+
    | 0 |
    | 1.2968142032623 |
    | 0 |
    | 0 |
    | 0 |
    +--------------------------+
    5 rows in set (0.01 sec)

    組み込みパーサーとは異なり、caseおよびinsensitivecase-insensitiveと一致していません。

24.2.4.5 デーモンプラグインの作成

デーモンプラグインは、サーバーによって実行されるがサーバーと通信しないコードに使用される単純なタイプのプラグインです。このセクションでは、MySQL ソース配布の plugin/daemon_example ディレクトリにあるプラグインの例を使用して、デーモンサーバープラグインを作成する方法について説明します。このディレクトリには、daemon_example という名前のデーモンプラグイン用の daemon_example.cc というソースファイルが格納されており、このデーモンプラグインは、データディレクトリ内の mysql-heartbeat.log という名前のファイルにハートビート文字列を定期的な間隔で書き込みます。

デーモンプラグインを作成するには、プラグインのソースファイルに次のヘッダーファイルをインクルードします。プラグインの機能および要件によっては、ほかの MySQL のヘッダーファイルまたは一般的なヘッダーファイルが必要になることもあります。

#include <mysql/plugin.h>

plugin.hMYSQL_DAEMON_PLUGIN サーバープラグインタイプおよびプラグインの宣言に必要なデータ構造体を定義します。

daemon_example.cc ファイルはライブラリディスクリプタを次のようにセットアップします。このライブラリディスクリプタには、単一の一般サーバープラグインディスクリプタが含まれています。

mysql_declare_plugin(daemon_example)
{ MYSQL_DAEMON_PLUGIN, &daemon_example_plugin, "daemon_example", "Brian Aker", "Daemon example, creates a heartbeat beat file in mysql-heartbeat.log", PLUGIN_LICENSE_GPL, daemon_example_plugin_init, daemon_example_plugin_deinit, 0x0100 , NULL, NULL, NULL, 0,
}
mysql_declare_plugin_end;

name メンバー (daemon_example) は、INSTALL PLUGINUNINSTALL PLUGIN などのステートメント内でプラグインを参照するために使用する名前を指定します。これは、SHOW PLUGINS または INFORMATION_SCHEMA.PLUGINS によって表示される名前でもあります。

プラグインディスクリプタの 2 番目のメンバー daemon_example_plugin は、タイプ固有のデーモンプラグインディスクリプタを指しています。この構造体は、タイプ固有の API バージョン番号のみで構成されています。

struct st_mysql_daemon daemon_example_plugin=
{ MYSQL_DAEMON_INTERFACE_VERSION };

タイプ固有の構造体にはインタフェース機能がありません。サーバーとプラグインの間の通信はありませんが、サーバーは一般プラグインディスクリプタから初期化関数および初期化解除関数を呼び出してプラグインを開始および停止します。

  • daemon_example_plugin_init() はハートビートファイルを開き、定期的に動作するスレッドを生成し、次のメッセージをファイルに書き込みます。

  • daemon_example_plugin_deinit() はファイルを閉じて、ほかのクリーンアップ処理を実行します。

プラグインライブラリのオブジェクトファイルをコンパイルおよびインストールするには、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」の手順を使用します。ライブラリファイルを使用するには、ライブラリファイルがプラグインディレクトリ (plugin_dir システム変数で指定されているディレクトリ) にインストールされている必要があります。daemon_example プラグインの場合、ソースから MySQL をビルドするときにコンパイルおよびインストールされます。これはバイナリ配布にも含められます。ビルド処理では、libdaemon_example.so という名前の共有オブジェクトライブラリが生成されます (サフィクスはプラットフォームによって異なる場合があります)。

プラグインを使用するには、プラグインをサーバーに登録します。たとえば、プラグインを実行時に登録するには、次のステートメントを使用します (必要に応じてサフィクスを変更します)。

mysql> INSTALL PLUGIN daemon_example SONAME 'libdaemon_example.so';

プラグインのロードについての追加情報は、セクション5.1.8.1「プラグインのインストールおよびアンインストール」を参照してください。

プラグインのインストールを検証するには、INFORMATION_SCHEMA.PLUGINS テーブルを調査するか、SHOW PLUGINS ステートメントを使用します。

プラグインがロードされると、プラグインはデータディレクトリ内の mysql-heartbeat.log という名前のファイルにハートビート文字列を定期的な間隔で書き込みます。このファイルは無制限に大きくなるため、プラグインが正しく動作することを確認したら、ファイルをアンロードしてください。

mysql> UNINSTALL PLUGIN daemon_example;

24.2.4.6 INFORMATION_SCHEMA プラグインの作成

このセクションでは、INFORMATION_SCHEMA テーブルサーバープラグインを作成する方法について説明します。このようなプラグインを実装するためのコード例については、MySQL ソース配布の sql/sql_show.cc ファイルを参照してください。InnoDB ソースにあるプラグインの例を参照することもできます。InnoDB ソースツリーの handler/i_s.cc ファイルおよび handler/ha_innodb.cc ファイルを参照してください (storage/innobase ディレクトリ内)。

INFORMATION_SCHEMA テーブルプラグインを作成するには、プラグインのソースファイルに次のヘッダーファイルをインクルードします。プラグインの機能および要件によっては、ほかの MySQL のヘッダーファイルまたは一般的なヘッダーファイルが必要になることもあります。

#include <sql_class.h>
#include <table.h>

これらのヘッダーファイルは、MySQL ソース配布の sql ディレクトリにあります。これらは C++ 構造体を含んでいるため、INFORMATION_SCHEMA プラグインのソースファイルは (C ではなく) C++ コードとしてコンパイルする必要があります。

ここで作成するプラグインの例のソースファイルは、simple_i_s_table.cc という名前です。これは、SIMPLE_I_S_TABLE という名前の単純な INFORMATION_SCHEMA テーブルを作成し、NAME および VALUE という名前の 2 つのカラムがあります。テーブルを実装するプラグインライブラリの一般ディスクリプタは、次のようになります。

mysql_declare_plugin(simple_i_s_library)
{ MYSQL_INFORMATION_SCHEMA_PLUGIN, &simple_table_info, "SIMPLE_I_S_TABLE", "Author Name", "Simple INFORMATION_SCHEMA table", PLUGIN_LICENSE_GPL, simple_table_init, NULL, 0x0100, NULL, NULL, NULL, 0
}
mysql_declare_plugin_end;

name メンバー (SIMPLE_I_S_TABLE) は、INSTALL PLUGINUNINSTALL PLUGIN などのステートメントでプラグインを参照するために使用する名前を指定します。これは、SHOW PLUGINS または INFORMATION_SCHEMA.PLUGINS によって表示される名前でもあります。

一般ディスクリプタの simple_table_info メンバーは、タイプ固有の API バージョン番号のみで構成されるタイプ固有のディスクリプタを指しています。

static struct st_mysql_information_schema simple_table_info =
{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };

一般ディスクリプタは、初期化関数および初期化解除関数を指しています。

  • 初期化関数は、テーブル構造体およびテーブルにデータを移入する関数についての情報を提供します。

  • 初期化解除関数は、必要なすべてのクリーンアップ処理を実行します。クリーンアップが不要な場合、このディスクリプタメンバーには (例に示されているように) NULL を指定できます。

初期化関数は、成功した場合は 0、およびエラーが発生した場合は 1 を返します。この関数は一般ポインタを受け取り、テーブル構造体へのポインタとして解釈します。

static int table_init(void *ptr)
{ ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE*)ptr; schema_table->fields_info= simple_table_fields; schema_table->fill_table= simple_fill_table; return 0;
}

この関数は、テーブル構造体の次の 2 つのメンバーを設定します。

  • fields_info: 各カラムについての情報を格納する ST_FIELD_INFO 構造体の配列。

  • fill_table: テーブルにデータを移入する関数。

fields_info によって指し示されている配列には、INFORMATION_SCHEMA のカラムごとの 1 つの要素および終端の要素が含まれるようにします。プラグイン例の次の simple_table_fields 配列は、SIMPLE_I_S_TABLE に 2 つのカラムがあることを示しています。NAME は長さ 10 バイトの文字列値であり、VALUE は表示幅が 20 バイトの整数値です。最後の構造体は、配列の終わりを示しています。

static ST_FIELD_INFO simple_table_fields[]=
{ {"NAME", 10, MYSQL_TYPE_STRING, 0, 0 0, 0}, {"VALUE", 6, MYSQL_TYPE_LONG, 0, MY_I_S_UNSIGNED, 0, 0}, {0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}
};

カラムの情報構造体については、table.h ヘッダーファイルの ST_FIELD_INFO の定義を参照してください。許容される MYSQL_TYPE_xxx タイプ値は、C API で使用される値です。セクション23.8.5「C API データ構造」を参照してください。

fill_table メンバーには、テーブルにデータを移入して、成功した場合は 0、およびエラーが発生した場合は 1 を返す関数を設定します。プラグインの例の場合、simple_fill_table() 関数は次のようになっています。

static int simple_fill_table(THD *thd, TABLE_LIST *tables, Item *cond)
{ TABLE *table= tables->table; table->field[0]->store("Name 1", 6, system_charset_info); table->field[1]->store(1); if (schema_table_store_record(thd, table)) return 1; table->field[0]->store("Name 2", 6, system_charset_info); table->field[1]->store(2); if (schema_table_store_record(thd, table)) return 1; return 0;
}

この関数は、INFORMATION_SCHEMA テーブルの各行の各カラムを初期化し、schema_table_store_record() を呼び出して行を生成します。store() メソッドの引数は、格納される値のタイプによって異なります。カラム 0 (NAME、文字列) の場合、store() は、文字列へのポインタ、その長さ、およびその文字列の文字セットに関する情報を受け取ります。

store(const char *to, uint length, CHARSET_INFO *cs);

カラム 1 (VALUE、整数) の場合、store() は値およびその値が符号なしであるかどうかを示すフラグを受け取ります。

store(longlong nr, bool unsigned_value);

INFORMATION_SCHEMA テーブルにデータを移入する方法に関するほかの例については、sql_show.ccschema_table_store_record() のインスタンスを検索してください。

プラグインライブラリのオブジェクトファイルをコンパイルおよびインストールするには、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」の手順を参照してください。ライブラリファイルを使用するには、ライブラリファイルがプラグインディレクトリ (plugin_dir システム変数で指定されているディレクトリ) にインストールされている必要があります。

プラグインをテストするには、プラグインをインストールします。

mysql> INSTALL PLUGIN SIMPLE_I_S_TABLE SONAME 'simple_i_s_table.so';

テーブルが存在することを確認します。

mysql> SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES -> WHERE TABLE_NAME = 'SIMPLE_I_S_TABLE';+------------------+
| TABLE_NAME |
+------------------+
| SIMPLE_I_S_TABLE |
+------------------+

選択を試行します。

mysql> SELECT * FROM INFORMATION_SCHEMA.SIMPLE_I_S_TABLE;+--------+-------+
| NAME | VALUE |
+--------+-------+
| Name 1 | 1 |
| Name 2 | 2 |
+--------+-------+

アンインストールします。

mysql> UNINSTALL PLUGIN SIMPLE_I_S_TABLE;

24.2.4.7 準同期レプリケーションプラグインの作成

このセクションでは、MySQL ソース配布の plugin/semisync ディレクトリにあるプラグイン例を使用して、準同期レプリケーションサーバープラグインを作成する方法について説明します。このディレクトリには、rpl_semi_sync_master および rpl_semi_sync_slave という名前のマスタープラグインおよびスレーブプラグインのソースファイルが格納されています。ここでは、プラグインフレームワークをセットアップする方法についてのみ説明します。プラグインがレプリケーション機能を実装する方法については、ソースを参照してください。

準同期レプリケーションプラグインを作成するには、プラグインソースファイルに次のヘッダーファイルをインクルードします。プラグインの機能および要件によっては、ほかの MySQL のヘッダーファイルまたは一般的なヘッダーファイルが必要になることもあります。

#include <mysql/plugin.h>

plugin.h は、MYSQL_REPLICATION_PLUGIN サーバープラグインタイプ、およびプラグインを宣言するために必要なデータ構造体を定義します。

マスター側の場合、semisync_master_plugin.cc には rpl_semi_sync_master という名前のプラグインの次の一般ディスクリプタが含まれています。

mysql_declare_plugin(semi_sync_master)
{ MYSQL_REPLICATION_PLUGIN, &semi_sync_master_plugin, "rpl_semi_sync_master", "He Zhenxing", "Semi-synchronous replication master", PLUGIN_LICENSE_GPL, semi_sync_master_plugin_init, semi_sync_master_plugin_deinit, 0x0100 , semi_sync_master_status_vars, semi_sync_master_system_vars, NULL, 0,
}
mysql_declare_plugin_end;

スレーブ側の場合、semisync_slave_plugin.cc には rpl_semi_sync_slave という名前のプラグインの次の一般ディスクリプタが含まれています。

mysql_declare_plugin(semi_sync_slave)
{ MYSQL_REPLICATION_PLUGIN, &semi_sync_slave_plugin, "rpl_semi_sync_slave", "He Zhenxing", "Semi-synchronous replication slave", PLUGIN_LICENSE_GPL, semi_sync_slave_plugin_init, semi_sync_slave_plugin_deinit, 0x0100 , semi_sync_slave_status_vars, semi_sync_slave_system_vars, NULL, 0,
}
mysql_declare_plugin_end;

マスタープラグインおよびスレーブプラグインのどちらの場合も、一般ディスクリプタには、タイプ固有のディスクリプタ、初期化関数と初期化解除関数、およびプラグインによって実装されるステータス変数とシステム変数へのポインタがあります。変数の設定については、セクション24.2.4.2.2「サーバープラグインのステータス変数およびシステム変数」を参照してください。以降の説明では、マスタープラグインのタイプ固有のディスクリプタ、初期化関数および初期化解除関数について記述していますが、スレーブプラグインにも同様に当てはまります。

マスターの一般ディスクリプタの semi_sync_master_plugin メンバーは、タイプ固有のディスクリプタを指しており、これはタイプ固有の API バージョン番号のみで構成されています。

struct Mysql_replication semi_sync_master_plugin= { MYSQL_REPLICATION_INTERFACE_VERSION
};

初期化関数および初期化解除関数の宣言は、次のようになります。

static int semi_sync_master_plugin_init(void *p);
static int semi_sync_master_plugin_deinit(void *p);

初期化関数はポインタを使用して、トランザクションおよびバイナリロギングのオブザーバーをサーバーに登録します。初期化が成功したあとに、サーバーは適切な時期にオブザーバーを呼び出します。(オブザーバーについては、ソースファイルを参照してください。)初期化解除関数は、オブザーバーを登録解除することによってクリーンアップ処理を実行します。各関数は、成功した場合は 0、およびエラーが発生した場合は 1 を返します。

プラグインライブラリのオブジェクトファイルをコンパイルおよびインストールするには、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」の手順を使用します。ライブラリファイルを使用するには、ライブラリファイルがプラグインディレクトリ (plugin_dir システム変数で指定されているディレクトリ) にインストールされている必要があります。rpl_semi_sync_master プラグインおよび rpl_semi_sync_slave プラグインの場合、これらはソースから MySQL をビルドするときにコンパイルおよびインストールされます。これらはバイナリ配布にも含まれています。ビルド処理では、semisync_master.so および semisync_slave.so という名前の共有オブジェクトライブラリが生成されます (サフィクスはプラットフォームによって異なる場合があります)。

24.2.4.8 監査プラグインの作成

このセクションでは、MySQL ソース配布の plugin/audit_null ディレクトリにあるプラグインの例を使用して、監査サーバープラグインを作成する方法について説明します。このディレクトリにある audit_null.c ソースファイルは、NULL_AUDIT という名前の簡単な監査プラグインの例を実装します。

サーバー内では、プラガブルな監査インタフェースは MySQL ソース配布の sql ディレクトリの sql_audit.h ファイルおよび sql_audit.cc ファイル内に実装されています。また、サーバー内のいくつかの場所は、監査可能なイベントが発生したときに監査インタフェースを呼び出すように変更されているため、登録された監査プラグインは必要に応じてイベントに関する通知を受けることができます。そのような呼び出しが行われる場所を確認するには、mysql_audit_xxx() という形式の名前を持つ関数の呼び出しを探してください。監査通知は、次のようなサーバーの動作に対して発生します。

  • 一般クエリーログへのメッセージの書き込み (ログが有効にされている場合)

  • エラーログへのメッセージの書き込み

  • クライアントへのクエリー結果の送信

  • クライアントの接続イベントおよび接続解除イベント

監査プラグインを作成するには、プラグインのソースファイルに次のヘッダーファイルをインクルードします。プラグインの機能および要件によっては、ほかの MySQL のヘッダーファイルまたは一般的なヘッダーファイルが必要になることもあります。

#include <mysql/plugin_audit.h>

plugin_audit.hplugin.h をインクルードするため、後者のファイルを明示的にインクルードする必要はありません。plugin.hMYSQL_AUDIT_PLUGIN サーバープラグインタイプおよびプラグインを宣言するために必要なデータ構造体を定義します。plugin_audit.h は、監査プラグインに固有のデータ構造体を定義します。

監査プラグインには、ほかの MySQL サーバープラグインと同様に、一般プラグインディスクリプタがあります (セクション24.2.4.2.1「サーバープラグインライブラリおよびプラグインディスクリプタ」を参照してください)。audit_null.c では、一般ディスクリプタは次のようになります。

mysql_declare_plugin(audit_null)
{ MYSQL_AUDIT_PLUGIN, &audit_null_descriptor, "NULL_AUDIT", "Oracle Corp", "Simple NULL Audit", PLUGIN_LICENSE_GPL, audit_null_plugin_init, audit_null_plugin_deinit, 0x0003, simple_status, NULL, NULL, 0,
}
mysql_declare_plugin_end;

name メンバー (NULL_AUDIT) は、INSTALL PLUGINUNINSTALL PLUGIN などのステートメントでプラグインを参照するために使用する名前を指定します。これは INFORMATION_SCHEMA.PLUGINS または SHOW PLUGINS によって表示される名前でもあります。

一般ディスクリプタは、SHOW STATUS ステートメントに対して各種のステータス変数を公開する構造体である simple_status も参照しています。

static struct st_mysql_show_var simple_status[]=
{ { "Audit_null_called", (char *) &number_of_calls, SHOW_INT }, { "Audit_null_general_log", (char *) &number_of_calls_general_log, SHOW_INT }, { "Audit_null_general_error", (char *) &number_of_calls_general_error, SHOW_INT }, { "Audit_null_general_result", (char *) &number_of_calls_general_result, SHOW_INT }, { "Audit_null_general_status", (char *) &number_of_calls_general_status, SHOW_INT }, { "Audit_null_connection_connect", (char *) &number_of_calls_connection_connect, SHOW_INT }, { "Audit_null_connection_disconnect", (char *) &number_of_calls_connection_disconnect, SHOW_INT }, { "Audit_null_connection_change_user", (char *) &number_of_calls_connection_change_user, SHOW_INT }, { 0, 0, 0}
};

audit_null_plugin_init 初期化関数は、プラグインがロードされたときにステータス変数をゼロに設定します。audit_null_plugin_deinit 関数は、プラグインがアンロードされるときにクリーンアップ処理を実行します。プラグインは動作中に、通知を受け取るたびに最初のステータス変数を増分します。また、イベントクラスおよびサブクラスに応じてほかのものも増分します。つまり、最初の変数はイベントサブクラスのカウントの総計となります。

一般ディスクリプタの audit_null_descriptor 値は、タイプ固有のディスクリプタを指しています。監査プラグインの場合、このディスクリプタの構造体は次のようになります。

struct st_mysql_audit
{ int interface_version; void (*release_thd)(MYSQL_THD); void (*event_notify)(MYSQL_THD, unsigned int, const void *); unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
};

タイプ固有のディスクリプタには次のメンバーがあります。

  • interface_version: 規則により、タイプ固有のプラグインディスクリプタは、特定のプラグインタイプのインタフェースバージョンで始まります。サーバーはプラグインをロードするときに interface_version を検査し、プラグインと互換性があるかどうかを確認します。監査プラグインの場合、interface_version メンバーの値は MYSQL_AUDIT_INTERFACE_VERSION (plugin_audit.h に定義されています) です。

  • release_thd: スレッドコンテキストとの関連付けが解除されていることをプラグインに通知するためにサーバーが呼び出す関数。そのような関数がない場合は、NULL を設定します。

  • event_notify: 監査可能イベントが発生したことをプラグインに通知するためにサーバーが呼び出す関数。この関数は NULL にはできません。監査が行われなくては意味がないためです。

  • class_mask: プラグインが通知を受け取るイベントクラスを示すビットマスク。この値が 0 の場合、サーバーはプラグインにイベントを渡しません。

サーバーは event_notify 関数および release_thd 関数を一緒に使用します。これらは特定のスレッドのコンテキストで呼び出され、スレッドはいくつかのイベント通知が生成されるアクティビティーを実行する場合があります。サーバーは、スレッドに対して event_notify を最初に呼び出しすときに、プラグインからスレッドへのバインドを作成します。このバインドが存在する間はプラグインをアンインストールできません。スレッドに対してイベントが発生しなくなると、サーバーは release_thd 関数を呼び出すことによってプラグインにこれを通知してからバインドを破棄します。たとえば、クライアントがステートメントを発行したとき、ステートメントを処理するスレッドは、ステートメントによって生成された結果セットとログに記録されたステートメントについて、監査プラグインに通知することがあります。これらの通知が実行されたあと、クライアントが別のステートメントを発行するまでの間、サーバーはスレッドをスリープ状態にする前にプラグインを解放します。

この設計により、プラグインは event_notify 関数への最初の呼び出しで対象となるスレッドに必要なリソースを割り当て、release_thd 関数でリソースを解放します。

event_notify function: if memory is needed to service the thread allocate memory ... rest of notification processing ...
release_thd function: if memory was allocated release memory ... rest of release processing ...

これは、通知関数でメモリーの割り当てと解放を繰り返すよりも効率的です。

NULL_AUDIT 監査プラグインの例では、タイプ固有のディスクリプタは次のようになります。

static struct st_mysql_audit audit_null_descriptor=
{ MYSQL_AUDIT_INTERFACE_VERSION, NULL, audit_null_notify, { (unsigned long) MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK }
};

サーバーは audit_null_notify を呼び出して監査イベント情報をプラグインに渡します。release_thd 関数はありません。

イベントクラスマスクは、generalクラスおよびconnectionクラスのすべてのイベントを対象とすることを示しています。plugin_audit.h は、これらのクラスとそれらに対応するクラスマスクのシンボルを定義しています。

#define MYSQL_AUDIT_GENERAL_CLASS 0
#define MYSQL_AUDIT_GENERAL_CLASSMASK (1 << MYSQL_AUDIT_GENERAL_CLASS)
#define MYSQL_AUDIT_CONNECTION_CLASS 1
#define MYSQL_AUDIT_CONNECTION_CLASSMASK (1 << MYSQL_AUDIT_CONNECTION_CLASS)

タイプ固有のディスクリプタでは、event_notify 関数プロトタイプの 2 番目と 3 番目のパラメータは、イベントクラス、およびイベント構造体への一般的なポインタを表しています。

void (*event_notify)(MYSQL_THD, unsigned int, const void *);

異なるクラスのイベントは異なる構造体を持つ場合があるため、通知関数はイベントクラス値を使用して、イベント構造へのポインタを解釈する方法を判断します。

イベントクラスが MYSQL_AUDIT_GENERAL_CLASS である通知関数をサーバーが呼び出す場合、サーバーは mysql_event_general 構造へのポインタとしてイベント構造体を渡します。

struct mysql_event_general
{ unsigned int event_subclass; int general_error_code; unsigned long general_thread_id; const char *general_user; unsigned int general_user_length; const char *general_command; unsigned int general_command_length; const char *general_query; unsigned int general_query_length; struct charset_info_st *general_charset; unsigned long long general_time; unsigned long long general_rows;
};

監査プラグインは mysql_event_general メンバーを次のように解釈できます。

  • event_subclass: 次のいずれかの値を持つイベントサブクラス。

    #define MYSQL_AUDIT_GENERAL_LOG 0
    #define MYSQL_AUDIT_GENERAL_ERROR 1
    #define MYSQL_AUDIT_GENERAL_RESULT 2
    #define MYSQL_AUDIT_GENERAL_STATUS 3
  • general_error_code: エラーコード。これは mysql_errno() C API 関数によって返されるものと似た値であり、0 はエラーなしを意味します。

  • general_thread_id: イベントが発生したスレッドの ID。

  • general_user: イベントの現在のユーザー。

  • general_user_length: general_user のバイト単位の長さ。

  • general_command: 一般クエリーログイベントの場合、操作の種類。たとえば、ConnectQueryShutdown です。エラーログイベントの場合、エラーメッセージ。これは mysql_error() C API 関数によって返されるものと似た値であり、空の文字列はエラーなしを意味します。結果イベントの場合、これは空です。

  • general_command_length: general_command のバイト単位の長さ。

  • general_query: ログに記録されたか結果を生成した SQL ステートメント。

  • general_query_length: general_query のバイト単位の長さ。

  • general_charset: イベントの文字セット情報。

  • general_time: 通知関数が呼び出された直前の時間を示す TIMESTAMP 値。

  • general_rows: 一般クエリーログイベントの場合、ゼロ。エラーログイベントの場合、エラーが発生した行番号。結果イベントの場合、結果の行数に 1 を加えた数値。結果セットを生成しないステートメントの場合、値は 0 です。このエンコードによって、結果セットを生成しないステートメントを、空の結果セットを生成するステートメントと区別できます。たとえば、DELETE ステートメントの場合、この値は 0 です。SELECT の場合、結果は常に 1 以上であり、1 は空の結果セットを表します。

  • general_host: 一般クエリーログイベントの場合、クライアントのホスト名を表す文字列。

  • general_sql_command: 一般クエリーログイベントの場合、connectdrop_table などの実行されるアクションの種類を示す文字列。

  • general_external_user: 一般クエリーログイベントの場合、外部ユーザーを表す文字列 (ない場合は空)。

  • general_ip: 一般クエリーログイベントの場合、クライアントの IP アドレスを表す文字列。

general_hostgeneral_sql_commandgeneral_external_user、および general_ip メンバーは、MySQL 5.6.14 で新しく導入されました。これらは、文字列とその長さがペアになった MYSQL_LEX_STRING 構造体です。たとえば、event_general が一般イベントへのポインタである場合、次のようにして general_host 値のメンバーにアクセスできます。

event_general->general_host.length
event_general->general_host.str

イベントクラスが MYSQL_AUDIT_CONNECTION_CLASS である通知関数をサーバーが呼び出す場合、サーバーは mysql_event_connection 構造へのポインタとしてイベント構造体を渡します。これは mysql_event_general 構造と似ており、ほぼ同じように解釈されます。

NULL_AUDIT プラグインの通知関数はきわめて単純です。これは、グローバルイベントカウンタを増分して、イベントクラスを判別し、イベントサブクラスを参照して増分するサブクラスカウンタを判別します。

static void audit_null_notify(MYSQL_THD thd __attribute__((unused)), unsigned int event_class, const void *event)
{ number_of_calls++; if (event_class == MYSQL_AUDIT_GENERAL_CLASS) { const struct mysql_event_general *event_general= (const struct mysql_event_general *) event; switch (event_general->event_subclass) { case MYSQL_AUDIT_GENERAL_LOG: number_of_calls_general_log++; break; case MYSQL_AUDIT_GENERAL_ERROR: number_of_calls_general_error++; break; case MYSQL_AUDIT_GENERAL_RESULT: number_of_calls_general_result++; break; case MYSQL_AUDIT_GENERAL_STATUS: number_of_calls_general_status++; break; default: break; } } else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) { const struct mysql_event_connection *event_connection= (const struct mysql_event_connection *) event; switch (event_connection->event_subclass) { case MYSQL_AUDIT_CONNECTION_CONNECT: number_of_calls_connection_connect++; break; case MYSQL_AUDIT_CONNECTION_DISCONNECT: number_of_calls_connection_disconnect++; break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: number_of_calls_connection_change_user++; break; default: break; } }
}

プラグインライブラリのオブジェクトファイルをコンパイルおよびインストールするには、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」の手順を使用します。ライブラリファイルを使用するには、ライブラリファイルがプラグインディレクトリ (plugin_dir システム変数で指定されているディレクトリ) にインストールされている必要があります。AUDIT_NULL プラグインの場合、ソースから MySQL をビルドするときにコンパイルおよびインストールされます。これはバイナリ配布にも含められます。ビルド処理では、adt_null.so という名前の共有オブジェクトライブラリが生成されます (サフィクスはプラットフォームによって異なる場合があります)。

プラグインを実行時に登録するには、次のステートメントを使用します (必要に応じてサフィクスを変更します)。

mysql> INSTALL PLUGIN NULL_AUDIT SONAME 'adt_null.so';

プラグインのロードについての追加情報は、セクション5.1.8.1「プラグインのインストールおよびアンインストール」を参照してください。

プラグインのインストールを検証するには、INFORMATION_SCHEMA.PLUGINS テーブルを調査するか、SHOW PLUGINS ステートメントを使用します。

監査プラグインがインストールされている間、監査プラグインはプラグインが呼び出されたイベントを示すステータス変数を公開します。

mysql> SHOW STATUS LIKE 'Audit_null%';+-----------------------------------+-------+
| Variable_name | Value |
+-----------------------------------+-------+
| Audit_null_called | 1388 |
| Audit_null_connection_change_user | 0 |
| Audit_null_connection_connect | 22 |
| Audit_null_connection_disconnect | 21 |
| Audit_null_general_error | 1 |
| Audit_null_general_log | 513 |
| Audit_null_general_result | 415 |
| Audit_null_general_status | 416 |
+-----------------------------------+-------+

Audit_null_called はすべてのイベントをカウントし、ほかの変数はイベントサブクラスのインスタンスをカウントします。たとえば、上記の SHOW STATUS ステートメントによって、サーバーは結果をクライアントに送信し、ログが有効にされている場合は一般クエリーログにメッセージを書き込みます。このため、クライアントがステートメントを繰り返し発行すると、Audit_null_called および Audit_null_general_result が毎回増分され、ログが有効にされている場合は Audit_null_general_log が増分されます。

プラグインをテスト後に無効にするには、次のステートメントを使用してプラグインをアンロードします。

mysql> UNINSTALL PLUGIN NULL_AUDIT;

24.2.4.9 認証プラグインの作成

MySQL ではプラガブルな認証がサポートされており、プラグインはクライアント接続を認証するために呼び出されます。認証プラグインを使用すると、mysql.user テーブルに格納されているパスワードによる組み込み方式ではない認証方式を使用できます。たとえば、外部認証方式にアクセスするプラグインを作成できます。また、認証プラグインはプロキシユーザー機能をサポートできるため、接続するユーザーを別のユーザーのプロキシにして、(アクセス制御のために) 別のユーザーの権限を持つユーザーとして扱うことが可能になります。詳細は、セクション6.3.7「プラガブル認証」およびセクション6.3.9「プロキシユーザー」を参照してください。

認証プラグインでは、サーバー側およびクライアント側のプラグインを作成します。サーバー側プラグインは、全文パーサープラグイン、監査プラグインなどのほかの種類のサーバープラグインで使用されるものと同じプラグイン API を使用します (ただし、タイプ固有のディスクリプタは異なります)。クライアント側プラグインはクライアントプラグイン API を使用します。

いくつかのヘッダーファイルに、認証プラグインに関する情報が格納されています。

  • plugin.h: MYSQL_AUTHENTICATION_PLUGIN サーバープラグインタイプを定義します。

  • client_plugin.h: クライアントプラグインの API を定義します。これには、クライアントプラグインディスクリプタ、およびクライアントプラグイン C API を呼び出すための関数プロトタイプが含まれます (セクション23.8.14「C API クライアントプラグイン関数」を参照してください)。

  • plugin_auth.h: 認証プラグインに固有のサーバープラグイン API の一部を定義します。これにはサーバー側認証プラグインのタイプ固有のディスクリプタと MYSQL_SERVER_AUTH_INFO 構造体が含まれます。

  • plugin_auth_common.h: クライアント認証プラグインとサーバー認証プラグインの共通要素が格納されます。これには戻り値の定義と MYSQL_PLUGIN_VIO 構造体が含まれます。

認証プラグインを作成するには、プラグインのソースファイルに次のヘッダーファイルをインクルードします。プラグインの機能および要件によっては、ほかの MySQL のヘッダーファイルまたは一般的なヘッダーファイルが必要になることもあります。

  • サーバー認証プラグインを実装するソースファイルの場合、次のファイルをインクルードします。

    #include <mysql/plugin_auth.h>
  • クライアント認証プラグインを実装するか、クライアントプラグインとサーバープラグインの両方を実装するソースファイルの場合、次のファイルをインクルードします。

    #include <mysql/plugin_auth.h>
    #include <mysql/client_plugin.h>
    #include <mysql.h>

plugin_auth.hplugin.h および plugin_auth_common.h をインクルードするため、後者のファイルを明示的にインクルードする必要はありません。

このセクションでは、連係して動作するサーバー認証プラグインとクライアント認証プラグインのペアを作成する方法について説明します。

警告

これらのプラグインは空でないあらゆるパスワードを受け入れ、パスワードは平文で送信されます。これは安全でないため、このプラグインは本番環境で使用しないでください

ここで作成するサーバー側プラグインおよびクライアント側プラグインの名前は、両方とも auth_simple です。セクション24.2.4.2「プラグインのデータ構造体」で説明したように、プラグインライブラリファイルはクライアントプラグインと同じベース名を持つ必要があるため、ソースファイル名は auth_simple.c であり、auth_simple.so という名前のライブラリが生成されます (システムで、ライブラリファイルのサフィクスとして .so が使用されていることを想定しています)。

MySQL ソース配布では、認証プラグインのソースが plugin/auth ディレクトリにあり、ほかの認証プラグインを作成するときのガイドとして参考にできます。また、組み込み認証プラグインが実装される方法を確認するには、MySQL サーバーに組み込まれるプラグインについては sql/sql_acl.cc、およびlibmysqlclient クライアントライブラリに組み込まれるプラグインについては sql-common/client.c を参照してください。(組み込みクライアントプラグインの場合、使用される auth_plugin_t 構造体は、通常のクライアントプラグイン宣言マクロで使用される構造と異なります。特に、最初の 2 つのメンバーは、宣言マクロによって設定されるのではなく、明示的に指定されています。)

24.2.4.9.1 サーバー側認証プラグインの作成

すべてのサーバープラグインタイプに使用される通常の一般ディスクリプタ形式を使用して、サーバー側プラグインを宣言します (セクション24.2.4.2.1「サーバープラグインライブラリおよびプラグインディスクリプタ」を参照してください)。auth_simple プラグインの場合、ディスクリプタは次のようになります。

mysql_declare_plugin(auth_simple)
{ MYSQL_AUTHENTICATION_PLUGIN, &auth_simple_handler, "auth_simple", "Author Name", "Any-password authentication plugin", PLUGIN_LICENSE_GPL, NULL, NULL, 0x0100, NULL, NULL, NULL, 0
}
mysql_declare_plugin_end;

name メンバー (auth_simple) は、INSTALL PLUGINUNINSTALL PLUGIN などのステートメントでプラグインを参照するために使用する名前を指定します。これは、SHOW PLUGINS または INFORMATION_SCHEMA.PLUGINS によって表示される名前でもあります。

一般ディスクリプタの auth_simple_handler メンバーはタイプ固有のディスクリプタを指しています。認証プラグインの場合、タイプ固有のディスクリプタは st_mysql_auth 構造体 (plugin_auth.h で定義されます) のインスタンスです。

struct st_mysql_auth
{ int interface_version; const char *client_auth_plugin; int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info);
};

st_mysql_auth 構造体には 3 つのメンバー (タイプ固有の API バージョン番号、クライアントプラグイン名、およびクライアントと通信するメインのプラグイン関数へのポインタ) があります。client_auth_plugin メンバーは、特定のプラグインが必要な場合にクライアントプラグインの名前を示すようにします。NULL 値は任意のプラグインを意味します。後者の場合、クライアントは任意のプラグインを使用できます。これは、クライアントプラグイン、およびクライアントプラグインが送信するユーザー名またはパスワードが、サーバープラグインの処理に関与しない場合に役立ちます。たとえば、サーバープラグインがローカルのクライアントのみを認証し、クライアントプラグインによって送信される情報ではなくオペレーティングシステムのいくつかのプロパティーを使用する場合です。

auth_simple の場合、タイプ固有のディスクリプタは次のようになります。

static struct st_mysql_auth auth_simple_handler =
{ MYSQL_AUTHENTICATION_INTERFACE_VERSION, "auth_simple", auth_simple_server
};

メインの関数は、I/O 構造体および MYSQL_SERVER_AUTH_INFO 構造体を表す 2 つの引数を受け取ります。この構造体の定義は plugin_auth.h にあります。これは次のような定義です。

typedef struct st_mysql_server_auth_info
{ char *user_name; unsigned int user_name_length; const char *auth_string; unsigned long auth_string_length; char authenticated_as[MYSQL_USERNAME_LENGTH+1]; char external_user[512]; int password_used; const char *host_or_ip; unsigned int host_or_ip_length;
} MYSQL_SERVER_AUTH_INFO;

文字列メンバーの文字セットは UTF-8 です。文字列に関連付けられた _length メンバーがある場合、これはバイト単位の文字列の長さを示します。文字列も NULL で終わります。

サーバーによって認証プラグインが呼び出されると、MYSQL_SERVER_AUTH_INFO 構造体メンバーが次のように解釈されます。これらの一部は、説明されているようにクライアントセッション内の SQL 関数またはシステム変数の値を設定するために使用されます。

  • user_name: クライアントによって送信されたユーザー名。この値は USER() 関数の値になります。

  • user_name_length: user_name のバイト単位の長さ。

  • auth_string: mysql.user テーブルの一致するアカウント名の行 (つまり、クライアントユーザー名およびホスト名が一致し、クライアントを認証する方法を判別するためにサーバーによって使用される行) の authentication_string カラムの値。

    次のステートメントを使用してアカウントを作成するとします。

    CREATE USER 'my_user'@'localhost' IDENTIFIED WITH my_plugin AS 'my_auth_string';

    my_user がローカルホストから接続する場合、サーバーは my_plugin を呼び出し、'my_auth_string'auth_string 値として渡します。

  • auth_string_length: auth_string のバイト単位の長さ。

  • authenticated_as: サーバーはこれをユーザー名 (user_name の値) に設定します。プラグインは、クライアントが別のユーザーの権限を持つことを示すように変更できます。たとえば、プラグインでプロキシユーザーがサポートされる場合、初期値は接続する (プロキシ) ユーザーの名前ですが、プラグインがこのメンバーをプロキシ設定されているユーザー名に変更できます。その後、サーバーはプロキシ設定されているユーザーの権限を持つものとしてプロキシユーザーを扱います (プロキシユーザーのサポートのためのほかの条件が満たされていることを想定しています。セクション24.2.4.9.4「認証プラグインでのプロキシユーザーサポートの実装」を参照してください)。この値は、最大 MYSQL_USER_NAME_LENGTH バイトの長さを持つ文字列、および終端の NULL として表されます。この値は CURRENT_USER() 関数の値になります。

  • external_user: サーバーは空の文字列 (NULL で終わります) をこれに設定します。この値は external_user システム変数の値になります。プラグインでこのシステム変数が異なる値を持つようにするには、プラグインでそのようにこのメンバーを設定してください (たとえば、接続しているユーザー名を設定します)。この値は、最大 511 バイトの長さの文字列、および終端の NULL として表されます。

  • password_used: このメンバーは、認証に失敗したときに適用されます。プラグインは、これを設定するか、無視できます。この値は、Authentication fails. Password used: %s という失敗のエラーメッセージを作成するために使用されます。password_used の値によって、次の表に示すように、%s の処理方法が決まります。

    password_used%s の処理
    0行わない
    1行う
    2%s がない
  • host_or_ip: 解決できる場合はクライアントのホスト名、または解決できない場合は IP アドレス。

  • host_or_ip_length: host_or_ip のバイト単位の長さ。

auth_simple のメイン関数 auth_simple_server() は、クライアントからパスワード (NULL で終了する文字列) を読み取り、パスワードが空でない (先頭バイトが NULL でない) 場合に成功します。

static int auth_simple_server (MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
{ unsigned char *pkt; int pkt_len; if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) return CR_ERROR; if (!pkt_len || *pkt == '\0') { info->password_used= PASSWORD_USED_NO; return CR_ERROR; } info->password_used= PASSWORD_USED_YES; return CR_OK;
}

メイン関数は、次の表に示すいずれかのエラーコードを返します。

エラーコード意味
CR_OK成功
CR_OK_HANDSHAKE_COMPLETEステータスパケットをクライアントに送信しない
CR_ERRORエラー
CR_AUTH_USER_CREDENTIALS認証エラー
CR_AUTH_HANDSHAKE認証ハンドシェイクエラー
CR_AUTH_PLUGIN_ERROR内部プラグインエラー

ハンドシェイクが動作する方法の例については、plugin/auth/dialog.c ソースファイルを参照してください。

CR_ERROR に続くエラーコードは MySQL 5.6.5 から使用できます。サーバーは、MySQL 5.6.5 以降で使用可能なパフォーマンススキーマ host_cache テーブル内のプラグインエラーをカウントします。

auth_simple_server_main() は非常に基本的なものであるため、パスワードを受け取ったかどうかを示すメンバーを設定することを除き、認証情報構造体を使用しません。

プロキシユーザーをサポートするプラグインは、プロキシ設定されているユーザー (クライアントユーザーが取得する権限を持っている MySQL ユーザー) の名前をサーバーに返す必要があります。これを行うには、プラグインはプロキシ設定されているユーザー名を info->authenticated_as メンバーに設定する必要があります。プロキシ設定については、セクション6.3.9「プロキシユーザー」およびセクション24.2.4.9.4「認証プラグインでのプロキシユーザーサポートの実装」を参照してください。

24.2.4.9.2 クライアント側認証プラグインの作成

mysql_declare_client_plugin() マクロおよび mysql_end_client_plugin マクロを使用して、クライアント側プラグインディスクリプタを宣言します (セクション24.2.4.2.3「クライアントプラグインディスクリプタ」を参照してください)。auth_simple プラグインの場合、ディスクリプタは次のようになります。

mysql_declare_client_plugin(AUTHENTICATION) "auth_simple", "Author Name", "Any-password authentication plugin", {1,0,0}, "GPL", NULL, NULL, NULL, NULL, auth_simple_client
mysql_end_client_plugin;

プラグイン名からオプション処理関数までのディスクリプタメンバーは、すべてのクライアントプラグインタイプで共通しています。(説明については、セクション24.2.4.2.3「クライアントプラグインディスクリプタ」を参照してください。)共通メンバーに続いて、このディスクリプタには認証プラグインに固有の追加メンバーがあります。これはメインの関数であり、サーバーとの通信を処理します。この関数は、I/O 構造体と接続ハンドラを表す 2 つの引数を取ります。任意のパスワードを指定できるこの単純なプラグインでは、メイン関数はユーザーが指定したパスワードをサーバーに書き込むだけです。

static int auth_simple_client (MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
{ int res; res= vio->write_packet(vio, (const unsigned char *) mysql->passwd, strlen(mysql->passwd) + 1); return res ? CR_ERROR : CR_OK;
}

メイン関数は、次の表に示すいずれかのエラーコードを返します。

エラーコード意味
CR_OK成功
CR_OK_HANDSHAKE_COMPLETE成功、クライアント完了
CR_ERRORエラー

CR_OK_HANDSHAKE_COMPLETE は、クライアントが自分の役割を正常に完了し、最後のパケットを読み取ったことを示します。認証プロトコルのラウンドトリップ数が事前に知られておらず、認証が完了したかどうかを判別するためにプラグインが別のパケットを読み取る必要がある場合、クライアントプラグインが CR_OK_HANDSHAKE_COMPLETE を返すことがあります。

24.2.4.9.3 認証プラグインの使用

プラグインライブラリのオブジェクトファイルをコンパイルおよびインストールするには、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」の手順を参照してください。ライブラリファイルを使用するには、ライブラリファイルがプラグインディレクトリ (plugin_dir システム変数で指定されているディレクトリ) にインストールされている必要があります。

サーバー側プラグインをサーバーに登録します。たとえば、プラグインをサーバー起動時にロードするには、--plugin-load=auth_simple.so オプションを使用します (システムで必要な場合はライブラリサフィクスを変更します)。

サーバーが認証のために auth_simple プラグインを使用する対象となるユーザーを作成します。

mysql> CREATE USER 'x'@'localhost' -> IDENTIFIED WITH auth_simple;

クライアントプログラムを使用して、ユーザー x としてサーバーに接続します。サーバー側の auth_simple プラグインはクライアント側の auth_simple プラグインを使用するクライアントプログラムと通信し、後者はサーバーにパスワードを送信します。サーバーは空のパスワードを送信する接続を拒否し、空ではないパスワードを送信する接続を受け入れます。これを検証するために、それぞれの方法でクライアントプログラムを起動します。

shell> mysql --user=x --skip-passwordERROR 1045 (28000): Access denied for user 'x'@'localhost' (using password: NO)
shell> mysql --user=x --password=abcmysql>

このサーバープラグインは空ではないパスワードをすべて受け入れるため、安全ではないとみなされます。プラグインをテストしてプラグインが機能することを検証したら、気づかずに安全でない認証プラグインがロードされたままの状態でサーバーが実行され続けないように、--plugin-load オプションを指定せずにサーバーを再起動します。また、DROP USER 'x'@'localhost' を使用してユーザーを削除します。

認証プラグインのロードおよび使用に関する追加情報については、セクション5.1.8.1「プラグインのインストールおよびアンインストール」およびセクション6.3.7「プラガブル認証」を参照してください。

認証プラグインの使用をサポートするクライアントプログラムを作成する場合、一般的にそのようなプログラムは、mysql_options() を呼び出して MYSQL_DEFAULT_AUTH オプションおよび MYSQL_PLUGIN_DIR オプションを設定することによって、プラグインをロードします。

char *plugin_dir = "path_to_plugin_dir";
char *default_auth = "plugin_name";
mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);
mysql_options(&mysql, MYSQL_DEFAULT_AUTH, default_auth);

一般にプログラムは、ユーザーがデフォルト値をオーバーライドできるようにする --plugin-dir および --default-auth オプションも受け付けます。

クライアントプログラムで低レベルのプラグイン管理が必要である場合は、クライアントライブラリに st_mysql_client_plugin 引数を受け取る関数を含めます。セクション23.8.14「C API クライアントプラグイン関数」を参照してください。

24.2.4.9.4 認証プラグインでのプロキシユーザーサポートの実装

プラガブルな認証によって使用できるようになる機能の 1 つはプロキシユーザーです (セクション6.3.9「プロキシユーザー」を参照してください)。サーバー側の認証プラグインでプロキシユーザーがサポートされるようにするには、次の条件を満たしている必要があります。

  • 接続するクライアントをプロキシユーザーとして扱う場合、プラグインは MYSQL_SERVER_AUTH_INFO 構造体の authenticated_as メンバー内に異なる名前を返して、プロキシ設定されているユーザー名を示す必要があります。external_user システム変数の値を設定するために、必要に応じて external_user メンバーを設定することもできます。

  • プロキシユーザーのアカウントは、プラグインによって認証されるようにセットアップする必要があります。アカウントをプラグインに関連付けるには、CREATE USER ステートメントまたは GRANT ステートメントを使用します。

  • プロキシユーザーアカウントには、プロキシ設定されているアカウントに対する PROXY 権限がある必要があります。この権限を付与するには、GRANT ステートメントを使用します。

言い換えると、プラグインに必要なプロキシユーザーサポートの特性は、プロキシ設定されているユーザー名を authenticated_as に設定することのみです。そのほかは、オプションであるか (external_user の設定)、SQL ステートメントを使用して DBA が行います。

認証プラグインはプロキシユーザーが接続したときに返すプロキシ設定されているユーザーをどのように決定するのでしょうか。これはプラグインによって異なります。通常、プラグインはサーバーから渡された認証文字列に基づいて、クライアントをプロキシ設定されているユーザーにマップします。この文字列は、認証にプラグインを使用することを指定する CREATE USER ステートメントの IDENTIFIED WITH 句の AS 部分に指定されます。

プラグイン開発者は認証文字列の構文ルールを決定し、これらのルールに従ってプラグインを実装します。外部ユーザーを MySQL ユーザーにマップするカンマ区切りのリストのペアをプラグインが受け取るとします。例:

CREATE USER ''@'%.example.com' IDENTIFIED WITH my_plugin AS 'extuser1=mysqlusera, extuser2=mysqluserb'
CREATE USER ''@'%.example.org' IDENTIFIED WITH my_plugin AS 'extuser1=mysqluserc, extuser2=mysqluserd'

サーバーがプラグインを起動してクライアントを認証するときに、適切な認証文字列をプラグインに渡します。プラグインには次の役割があります。

  1. 文字列を構文解析して構成要素に分解し、使用するマッピングを決定する

  2. クライアントユーザー名とマッピングを比較する

  3. 適切な MySQL ユーザー名を返す

たとえば、example.com ホストから extuser2 が接続する場合、サーバーは 'extuser1=mysqlusera, extuser2=mysqluserb' をプラグインに渡し、プラグインは mysqluserb に終端の NULL バイトを付加して authenticated_as にコピーします。example.org ホストから extuser2 が接続する場合、サーバーは 'extuser1=mysqluserc, extuser2=mysqluserd' を渡し、プラグインは代わりに mysqluserd をコピーします。

マッピングに一致するものがない場合のアクションはプラグインによって異なります。一致するものがある必要があるとき、多くの場合、プラグインはエラーを返します。または、プラグインが単純にクライアント名を返すこともあり、この場合、プラグインは authenticated_as を変更せず、サーバーはクライアントをプロキシとして扱いません。

次の例では、auth_simple_proxy という名前のプラグインを使用して、プロキシユーザーを処理する方法を示しています。前に示した auth_simple プラグインのように、auth_simple_proxy は空ではないすべてのパスワードを有効なものとして受け入れます (このため、本番環境で使用しないでください)。また、これは auth_string 認証文字列メンバーを検査し、次の非常に単純なルールを使用してこれを解釈します。

  • 文字列が空の場合、プラグインは指定されたユーザー名を返し、プロキシ設定は行われません。つまり、プラグインは authenticated_as の値を変更せずにそのままにします。

  • 文字列が空ではない場合、プラグインはこれをプロキシ設定されているユーザーの名前として扱い、プロキシ設定が行われるようにこれを authenticated_as にコピーします。

テストを行うために、上記のルールに従ってプロキシ設定されていない 1 つのアカウントとプロキシ設定されている 1 つのアカウントをセットアップします。これは、一方のアカウントには AS 句がなく、他方のアカウントにはプロキシ設定されているユーザーを指定する AS 句があることを意味します。

CREATE USER 'plugin_user1'@'localhost' IDENTIFIED WITH auth_simple_proxy;
CREATE USER 'plugin_user2'@'localhost' IDENTIFIED WITH auth_simple_proxy AS 'proxied_user';

また、プロキシ設定されているユーザーのアカウントを作成し、そのアカウントに対する PROXY 権限を plugin_user2 に付与します。

CREATE USER 'proxied_user'@'localhost' IDENTIFIED BY 'proxied_user_pass';
GRANT PROXY ON 'proxied_user'@'localhost' TO 'plugin_user2'@'localhost';

サーバーは、認証プラグインを呼び出す前に、authenticated_as にクライアントユーザー名を設定します。ユーザーがプロキシであることを示すために、プラグインはプロキシ設定されているユーザー名を authenticated_as に設定します。auth_simple_proxy の場合、これは、auth_string 値を検査し、値が空でない場合は値を authenticated_as メンバーにコピーして、プロキシ設定されているユーザーの名前としてそれを返す必要があることを意味します。また、プロキシ設定が行われる場合、プラグインは external_user メンバーにクライアントユーザー名を設定し、これが external_user システム変数の値になります。

static int auth_simple_proxy_server (MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
{ unsigned char *pkt; int pkt_len; if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) return CR_ERROR; if (!pkt_len || *pkt == '\0') { info->password_used= PASSWORD_USED_NO; return CR_ERROR; } info->password_used= PASSWORD_USED_YES; if (info->auth_string_length > 0) { strcpy (info->authenticated_as, info->auth_string); strcpy (info->external_user, info->user_name); } return CR_OK;
}

接続が成功すると、USER() 関数は接続しているクライアントユーザーとホスト名を示し、CURRENT_USER() はセッション中に適用される権限を持つアカウントを示します。後者の値は、プロキシ設定が行われない場合は接続するユーザーアカウント、およびプロキシ設定が行われた場合はプロキシ設定されているアカウントになります。

プラグインをコンパイルおよびインストールして、テストします。最初に、plugin_user1 として接続します。

shell> mysql --user=plugin_user1 --password=x

この場合、プロキシ設定は行われません。

mysql> SELECT USER(), CURRENT_USER(), @@proxy_user, @@external_user\G*************************** 1. row *************************** USER(): plugin_user1@localhost CURRENT_USER(): plugin_user1@localhost @@proxy_user: NULL
@@external_user: NULL

次に、plugin_user2 として接続します。

shell> mysql --user=plugin_user2 --password=x

この場合、plugin_user2proxied_user に対してプロキシ設定されます。

mysql> SELECT USER(), CURRENT_USER(), @@proxy_user, @@external_user\G*************************** 1. row *************************** USER(): plugin_user2@localhost CURRENT_USER(): proxied_user@localhost @@proxy_user: 'plugin_user2'@'localhost'
@@external_user: 'plugin_user2'@'localhost'

24.2.4.10 パスワード検証プラグインの作成

このセクションでは、パスワード検証サーバープラグインを作成する方法について説明します。この手順は、MySQL ソース配布の plugin/password_validation ディレクトリにあるソースコードに基づいています。そのディレクトリ内にある validate_password.cc ソースファイルは、validate_password という名前のプラグインを実装します。

パスワード検証プラグインを作成するには、プラグインのソースファイルに次のヘッダーファイルをインクルードします。プラグインの機能および要件によっては、ほかの MySQL のヘッダーファイルまたは一般的なヘッダーファイルが必要になることもあります。

#include <mysql/plugin_validate_password.h>

plugin_validate_password.hplugin.h をインクルードするため、後者のファイルを明示的にインクルードする必要はありません。plugin.hMYSQL_VALIDATE_PASSWORD_PLUGIN サーバープラグインタイプとプラグインを宣言するために必要なデータ構造体を定義します。plugin_validate_password.h は、パスワード検証プラグインに固有のデータ構造体を定義します。

パスワード検証プラグインには、ほかの MySQL サーバープラグインと同じように一般プラグインディスクリプタがあります (セクション24.2.4.2.1「サーバープラグインライブラリおよびプラグインディスクリプタ」を参照してください)。validate_password.cc では、一般ディスクリプタは次のようになります。

mysql_declare_plugin(validate_password)
{ MYSQL_VALIDATE_PASSWORD_PLUGIN, &validate_password_descriptor, "validate_password", "Oracle Corporation", "check password strength", PLUGIN_LICENSE_GPL, validate_password_init, validate_password_deinit, 0x0100, NULL, validate_password_system_variables, NULL, 0,
}
mysql_declare_plugin_end;

name メンバー (validate_password) は、INSTALL PLUGINUNINSTALL PLUGIN などのステートメントでプラグインを参照するために使用する名前を指定します。これは INFORMATION_SCHEMA.PLUGINS または SHOW PLUGINS によって表示される名前でもあります。

一般ディスクリプタは、SHOW VARIABLES ステートメントに対してさまざまなステータス変数を公開する構造体である validate_password_system_variables も参照します。

static struct st_mysql_sys_var* validate_password_system_variables[]= { MYSQL_SYSVAR(length), MYSQL_SYSVAR(number_count), MYSQL_SYSVAR(mixed_case_count), MYSQL_SYSVAR(special_char_count), MYSQL_SYSVAR(policy), MYSQL_SYSVAR(dictionary_file), NULL
};

validate_password_init 初期化関数は辞書ファイルを読み取り (指定されている場合)、validate_password_deinit 関数はそのファイルに関連付けられているデータ構造体を解放します。

一般ディスクリプタの validate_password_descriptor 値はタイプ固有のディスクリプタを指しています。パスワード検証プラグインの場合、このディスクリプタの構造体は次のようになります。

struct st_mysql_validate_password
{ int interface_version; int (*validate_password)(mysql_string_handle password); int (*get_password_strength)(mysql_string_handle password);
};

タイプ固有のディスクリプタには次のメンバーがあります。

  • interface_version: 規則により、タイプ固有のプラグインディスクリプタは、特定のプラグインタイプのインタフェースバージョンで始まります。サーバーはプラグインをロードするときに interface_version を検査し、プラグインと互換性があるかどうかを確認します。パスワード検証プラグインの場合、interface_version メンバーの値は MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION (plugin_validate_password.h で定義されます) です。

  • validate_password: パスワードが現在のパスワードポリシーを満たしているかどうかをテストするためにサーバーが呼び出す関数。パスワードが妥当な場合は 1 を返し、そうでない場合は 0 を返します。引数はパスワードであり、mysql_string_handle 値として渡されます。このデータ型は mysql_string サーバーサービスによって実装されます。詳細は、sql ディレクトリ内の string_service.h および string_service.cc のソースファイルを参照してください。

  • get_password_strength: パスワードの強さを評価するためにサーバーが呼び出す関数。これは 0 (弱い) から 100 (強い) までの値を返します。引数はパスワードであり、mysql_string_handle 値として渡されます。

validate_password プラグインの場合、タイプ固有のディスクリプタは次のようになります。

static struct st_mysql_validate_password validate_password_descriptor=
{ MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION, validate_password, get_password_strength
};

プラグインライブラリのオブジェクトファイルをコンパイルおよびインストールするには、セクション24.2.4.3「プラグインライブラリのコンパイルおよびインストール」の手順を使用します。ライブラリファイルを使用するには、ライブラリファイルがプラグインディレクトリ (plugin_dir システム変数で指定されているディレクトリ) にインストールされている必要があります。validate_password プラグインの場合、MySQL をソースからビルドするときにコンパイルおよびインストールされます。これはバイナリ配布にも含められます。ビルド処理では、validate_password.so という名前の共有オブジェクトライブラリが生成されます (拡張子はプラットフォームによって異なる場合があります)。

プラグインを実行時に登録するには、次のステートメントを使用します (必要に応じて拡張子を変更します)。

mysql> INSTALL PLUGIN validate_password SONAME 'validate_password.so';

プラグインのロードについての追加情報は、セクション5.1.8.1「プラグインのインストールおよびアンインストール」を参照してください。

プラグインのインストールを検証するには、INFORMATION_SCHEMA.PLUGINS テーブルを調査するか、SHOW PLUGINS ステートメントを使用します。

validate_password プラグインがインストールされている間、プラグインはパスワード検査パラメータを示すシステム変数を公開します。

mysql> SHOW VARIABLES LIKE 'validate_password%';+--------------------------------------+--------+
| Variable_name | Value |
+--------------------------------------+--------+
| validate_password_dictionary_file | |
| validate_password_length | 8 |
| validate_password_mixed_case_count | 1 |
| validate_password_number_count | 1 |
| validate_password_policy | MEDIUM |
| validate_password_special_char_count | 1 |
+--------------------------------------+--------+

これらの変数の説明については、セクション6.1.2.6.2「パスワード検証プラグインのオプションおよび変数」を参照してください。

プラグインをテスト後に無効にするには、次のステートメントを使用してプラグインをアンロードします。

mysql> UNINSTALL PLUGIN validate_password;

24.2.5 プラグインのための MySQL サービス

MySQL サーバープラグインは、サーバーのサービスにアクセスできます。サービスインタフェースは、プラグインが呼び出すことができるサーバー機能を公開します。これはプラグイン API を補完し、次の特徴があります。

  • サービスによって、プラグインは通常の関数呼び出しを使用して、サーバー内部のコードにアクセスできます。

  • サービスには移植性があり、複数のプラットフォームで動作します。

  • インタフェースにはバージョン管理メカニズムがあるため、サーバーによってサポートされるサービスバージョンを、プラグインバージョンに対してロード時にチェックできます。バージョン管理により、サーバーが提供するサービスのバージョン、およびプラグインが予期または必要とするサービスのバージョンの間の互換性の問題が防止されます。

現在のサービスには次のものが含まれており、ほかのサービスも実装できます。

  • my_plugin_log_service: プラグインがエラーの報告およびエラーメッセージの指定を行うことができるサービス。サーバーはエラーログにメッセージを書き込みます。

  • my_snprintf: プラットフォーム間で一貫性のある結果を生成する、文字列の書式設定サービス。

  • my_thd_scheduler: プラグインがスレッドスケジューラを選択するためのサービス。

  • mysql_string: 文字列操作のためのサービス。

  • thd_alloc: メモリー割り当てサービス。

  • thd_wait: プラグインがスリープまたは停止することを報告するためのサービス。

プラグインサービスインタフェースとプラグイン API の違いは次のとおりです。

  • プラグイン API では、サーバーからプラグインを使用できます。呼び出しを開始するのは、プラグインを呼び出すサーバーです。これにより、サーバー機能の拡張、またはサーバーの処理に関する通知を受け取るための登録をプラグインで行うことができます。

  • プラグインサービスインタフェースでは、プラグインはサーバー内部のコードを呼び出すことができます。呼び出しを開始するのは、サービス関数を呼び出すプラグインです。これにより、多くのプラグインがサーバーにすでに実装されている機能を使用できるようになり、プラグインに個別に機能を実装する必要がなくなります。

サーバーを変更して新しいサービスを追加する開発者は、プラグインのための MySQL サービスを参照してください。

このセクションの残りの部分では、サービスとして使用可能なサーバー機能をプラグインで使用する方法について説明します。my_snprintf サービスを使用するデーモンプラグインの例のソースも参照してください。このプラグインは、MySQL ソース配布の plugin/daemon_example ディレクトリにあります。

存在するサービスおよびサービスが提供する関数を確認するには、MySQL ソース配布の include/mysql ディレクトリ内を参照してください。関連するファイルは次のとおりです。

  • plugin.hservices.h をインクルードします。

  • services.h は、使用可能なすべてのサービス固有のヘッダーファイルをインクルードする包括的なヘッダーです。

  • サービス固有のヘッダーには、service_my_snprintf.h または service_thd_alloc.h のような名前が付いています。

各サービス固有ヘッダーには、対象となるサービスの完全な使用法を示すドキュメントを提供するコメントがあり、使用可能なサービス関数、それらの呼び出しシーケンス、および戻り値が含まれています。

プラグイン内からサービスを使用するには、サービス関連の情報にアクセスするための plugin.h ヘッダーファイルをプラグインのソースファイルでインクルードする必要があります。

#include <mysql/plugin.h>

これによってセットアップのコストが増えるわけではありません。このファイルにはすべてのプラグインで必要となる定義および構造体が含まれているため、プラグインはいずれにしてもこのファイルをインクルードする必要があります。

サービスにアクセスするために、プラグインはほかの関数と同様にサービス関数を呼び出します。たとえば、出力のために文字列を書式設定してバッファーに入れるには、同じ名前のサービスによって提供されている my_snprintf() 関数を呼び出します。

char buffer[BUFFER_SIZE];
my_snprintf(buffer, sizeof(buffer), format_string, argument_to_format, ...);

サーバーがエラーログに書き込むエラーを報告するには、最初にエラーレベルを選択します。mysql/service_my_plugin_log.h はこれらのレベルを定義しています。

enum plugin_log_level
{ MY_ERROR_LEVEL, MY_WARNING_LEVEL, MY_INFORMATION_LEVEL
}; 

次に、my_plugin_log_message() を呼び出します。

int my_plugin_log_message(MYSQL_PLUGIN *plugin, enum plugin_log_level level, const char *format, ...);

例:

my_plugin_log_message(plugin_ptr, MY_ERROR_LEVEL, "Cannot initialize plugin");

プラグインをビルドするときに、libmysqlservices ライブラリでリンクする必要があります。リンク時には -lmysqlservices フラグを使用します。たとえば CMake の場合、最上位レベルの CMakeLists.txt ファイルに次の内容を指定します。

FIND_LIBRARY(MYSQLSERVICES_LIB mysqlservices PATHS "${MYSQL_SRCDIR}/libservices" NO_DEFAULT_PATH)

プラグインのソースが格納されているディレクトリの CMakeLists.txt ファイルに次の内容を指定します。

# the plugin needs the mysql services library for error logging
TARGET_LINK_LIBRARIES (your_plugin_library_name ${MYSQLSERVICES_LIB})

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

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

  • ユーザー定義関数 (UDF) インタフェースを使用して関数を追加できます。ユーザー定義関数はオブジェクトファイルとしてコンパイルされ、CREATE FUNCTION ステートメントおよび DROP FUNCTION ステートメントを使用して、サーバーに対して動的に追加および削除されます。セクション13.7.3.1「ユーザー定義関数のための CREATE FUNCTION 構文」を参照してください。

  • 関数をネイティブ (組み込み) MySQL 関数として追加できます。ネイティブ関数はコンパイルされて mysqld サーバー内に組み込まれ、永続的に使用できます。

  • 関数を追加するもう 1 つの方法は、ストアドファンクションを作成することです。これらは、オブジェクトコードをコンパイルするのではなく、SQL ステートメントを使用して記述します。ストアドファンクションを記述するための構文は、ここでは説明しません。セクション20.2「ストアドルーチン (プロシージャーと関数) の使用」を参照してください。

コンパイルされた関数を作成するための各方法には、長所と短所があります。

  • ユーザー定義関数を作成する場合、サーバー自体のほかにオブジェクトファイルをインストールする必要があります。関数をコンパイルしてサーバーに配置する場合、それを行う必要はありません。

  • ネイティブ関数の場合は、ソース配布を変更する必要があります。UDF の場合はその必要はありません。UDF をバイナリの MySQL 配布に追加できます。MySQL のソースにアクセスする必要はありません。

  • MySQL の配布をアップグレードする場合、UDF インタフェースが変更される新しいバージョンにアップグレードしないかぎり、以前インストールした UDF を使用し続けることができます。ネイティブ関数の場合、アップグレードするたびに変更を繰り返す必要があります。

どのような方法を使用して新しい関数を追加したかにかかわらず、これらの関数は、ABS()SOUNDEX() などのネイティブ関数と同じように SQL ステートメントから呼び出すことができます。

各種の関数への参照をサーバーが解釈する方法を記述したルールについては、セクション9.2.4「関数名の構文解析と解決」を参照してください。

以降のセクションでは、UDF インタフェースの機能、UDF を作成するための手順、UDF の誤用を防ぐために MySQL が行うセキュリティー予防措置、およびネイティブな MySQL 関数を追加する方法について説明します。

UDF を作成する方法を示すソースコードの例については、MySQL ソース配布に提供されている sql/udf_example.cc ファイルを参照してください。

24.3.1 ユーザー定義関数インタフェースの機能

ユーザー定義関数のための MySQL インタフェースには次の機能があります。

  • 関数は、文字列値、整数値、または実数値を返すことができ、同じタイプの引数を受け入れることができます。

  • 一度に単一の行を操作する単純な関数、または行のグループを操作する集約関数を定義できます。

  • 関数に渡される引数の数、タイプ、および名前をチェックできる情報が関数に渡されます。

  • 引数が関数に渡される前に引数を特定のタイプに強制的に変更するように MySQL に指示できます。

  • 関数が NULL を返すこと、またはエラーが発生したことを示すことができます。

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

UDF のメカニズムが機能するためには、関数を C または C++ で記述し、オペレーティングシステムが動的ロードをサポートしている必要があります。MySQL ソース配布には、5 つの UDF 関数を定義している sql/udf_example.cc ファイルが含まれています。UDF の呼び出し規則のしくみを理解するには、このファイルを参照してください。include/mysql_com.h ヘッダーファイルには UDF 関連のシンボルおよびデータ構造体が定義されていますが、このヘッダーファイルを直接インクルードする必要はなく、mysql.h によってインクルードされます。

UDF に含まれているコードは実行中のサーバーの一部になるため、UDF を記述するときは、サーバーコードを記述するときに適用されるすべての制約に従う必要があります。たとえば、libstdc++ ライブラリの関数を使用しようとすると、問題が生じることがあります。これらの制約は将来のサーバーのバージョンで変更されることがあるため、サーバーをアップグレードするときに、古いサーバー用にもともと作成された UDF を改訂する必要がある場合があります。これらの制約については、セクション2.9.4「MySQL ソース構成オプション」およびセクション2.9.5「MySQL のコンパイルに関する問題」を参照してください。

UDF を使用できるようにするには、mysqld を動的にリンクする必要があります。mysqld からシンボルにアクセスする必要がある UDF を使用する場合 (たとえば、sql/udf_example.ccmetaphone 関数は default_charset_info を使用します) は、-rdynamic を指定してプログラムをリンクする必要があります (man dlopen を参照してください)。

SQL ステートメントで使用する各関数について、対応する C (または C++) 関数を定義します。以降の説明では、xxxという名前を関数名の例として使用します。SQL と C/C++ での使用を区別するために、XXX() (大文字) は SQL 関数の呼び出しを示し、xxx() (小文字) は C/C++ 関数の呼び出しを示します。

注記

C++ を使用する場合は、C 関数を次のようにカプセル化できます。

extern "C" { ... }

これにより、完成した UDF 内で C++ 関数名が読み取ることができる状態に維持されます。

次のリストは、XXX() という名前の関数のインタフェースを実装するために記述する C/C++ 関数について説明しています。メインの関数 xxx() は必須です。また、セクション24.3.2.6「ユーザー定義関数のセキュリティー上の予防措置」で説明している理由により、UDF にはここで説明している少なくとも 1 つのほかの関数が必要となります。

  • xxx()

    メイン関数。ここで関数の結果が計算されます。SQL 関数のデータ型と C/C++ 関数の戻り型との対応を次に示します。

    SQL の型C/C++ の型
    STRINGchar *
    INTEGERlong long
    REALdouble

    DECIMAL 関数を宣言することも可能ですが、現時点では値は文字列として返されるため、STRING 関数の場合と同様に UDF を作成します。ROW 関数は実装されていません。

  • xxx_init()

    xxx() の初期化関数。存在する場合は次の目的で使用できます。

    • XXX() への引数の数をチェックする。

    • 引数が目的の型であることを確認するか、メインの関数が呼び出されたときに引数を目的の型に強制的に変更するように MySQL に指示する。

    • メインの関数が必要とするメモリーを割り当てる。

    • 結果の最大長を指定する。

    • 結果の小数点以下の最大の桁数を指定する (REAL 関数の場合)。

    • 結果として NULL を許容するかどうかを指定する。

  • xxx_deinit()

    xxx() の初期化解除関数。存在する場合、初期化関数によって割り当てられたすべてのメモリーを割り当て解除します。

SQL ステートメントによって XXX() が呼び出されると、MySQL は初期化関数 xxx_init() を呼び出して、引数のチェック、メモリーの割り当てなどの必要なセットアップを実行させます。xxx_init() がエラーを返した場合、MySQL はエラーメッセージを出力して SQL ステートメントを中止し、メイン関数または初期化解除関数を呼び出しません。それ以外の場合、MySQL はメイン関数 xxx() を行ごとに 1 回ずつ呼び出します。すべての行が処理されると、MySQL は初期化解除関数 xxx_deinit() を呼び出し、必要なクリーンアップ処理が実行されます。

SUM() のように動作する集約関数の場合は、次の関数も作成する必要があります。

  • xxx_clear()

    現在の集約値をリセットしますが、新しいグループに対する初期集計値として引数を挿入しません。

  • xxx_add()

    現在の集計値に引数を追加します。

MySQL は集計 UDF を次のように処理します。

  1. xxx_init() を呼び出して、集約関数が結果を格納するために必要なメモリーを割り当てます。

  2. GROUP BY 式に従ってテーブルをソートします。

  3. 新しいグループになるたびに先頭行で xxx_clear() を呼び出します。

  4. 同じグループに属する各行に対して xxx_add() を呼び出します。

  5. グループが変更されたとき、または最後の行が処理されたあとに、xxx() を呼び出して集約の結果を取得します。

  6. すべての行が処理されるまでステップ 3 から 5 までを繰り返します。

  7. xxx_deinit() を呼び出して、UDF が割り当てたメモリーを解放します。

すべての関数はスレッドセーフである必要があります。これにはメイン関数だけでなく、初期化関数および初期化解除関数のほか、集約関数によって必要とされる追加の関数も含まれます。この要件により、変化するグローバル変数または静的変数を割り当てることができなくなります。メモリーが必要な場合は、メモリーを xxx_init() で割り当て、xxx_deinit() で解放するようにしてください。

24.3.2.1 単純な関数のための UDF の呼び出しシーケンス

このセクションでは単純な UDF を作成するときに定義する必要があるさまざまな関数について説明します。セクション24.3.2「新しいユーザー定義関数の追加」には、MySQL がこれらの関数を呼び出す順序が記載されています。

メインの xxx() 関数は、このセクションに示すように宣言してください。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);

DECIMAL 関数は文字列値を返すため、STRING 関数と同様に宣言してください。ROW 関数は実装されていません。

初期化関数および初期化解除関数は、次のように宣言します。

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()NULL を返すことができる場合、xxx_init()maybe_null1 に設定します。いずれかの引数が maybe_null として宣言されている場合、デフォルト値は 1 です。

  • unsigned int decimals

    小数点の右側の桁数。デフォルト値は、メイン関数に渡された引数の小数点以下の桁数の最大値です。たとえば、関数に 1.341.345、および 1.3 が渡された場合、1.345 の小数点以下の桁数が 3 であるため、デフォルトは 3 になります。

    引数で小数点以下の桁数が固定されていない場合、decimals 値は 31 に設定され、これは DECIMALFLOAT、および DOUBLE データ型に許可される最大の小数点以下の桁数に 1 を加えた数です。MySQL 5.6 では、この値は mysql_com.h ヘッダーファイルの定数 NOT_FIXED_DEC として利用できます。

    decimals 値の 31 は、FLOAT カラムまたは DOUBLE カラムが小数点以下の桁数を明示せずに宣言された (たとえば、FLOAT(10,3) でなく FLOAT) 場合の引数、または 1345E-3 などの浮動小数点の定数に使用されます。これは、関数内で数値形式に変換される可能性がある、文字列およびその他の数値以外の引数に対しても使用されます。

    decimals メンバーを初期化する値は、デフォルトにすぎません。これは、実行される実際の計算が反映されるように関数内で変更できます。デフォルトは、引数の最大の小数点以下の桁数が使用されるように決定します。いずれかの引数の小数点以下の桁数が NOT_FIXED_DEC である場合、その値が decimals に使用されます。

  • unsigned int max_length

    結果の最大長。デフォルトの max_length 値は、関数の結果の型に応じて異なります。文字列関数の場合、デフォルトはもっとも長い引数の長さです。整数関数の場合、デフォルトは 21 桁です。実数関数の場合、デフォルトは initid->decimals によって示されている小数点以下の桁数に 13 を加えた値です。(数値関数の場合、長さには符号文字または小数点文字が含まれています。)

    BLOB 値を返す場合は、max_length を 65K バイトまたは 16M バイトに設定できます。このメモリーは割り当てられませんが、この値は、データを一時的に格納する必要がある場合に使用するデータ型を決定するために使用されます。

  • char *ptr

    関数が独自の用途に使用できるポインタ。たとえば、複数の関数間で initid->ptr を使用して、割り当て済みのメモリーをやり取りできます。xxx_init() でこのメモリーを割り当てて、それをこのポインタに割り当てます。

    initid->ptr = allocated_memory;

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

  • my_bool const_item

    xxx_init() は、xxx() が常に同じ値を返す場合は const_item1 に設定し、それ以外の場合は 0 に設定します。

24.3.2.2 集約関数のための UDF の呼び出しシーケンス

このセクションでは集約 UDF を作成するときに定義する必要があるさまざまな関数について説明します。セクション24.3.2「新しいユーザー定義関数の追加」には、MySQL がこれらの関数を呼び出す順序が記載されています。

  • xxx_reset()

    この関数は、MySQL が新しいグループ内で最初の行を見つけたときに呼び出されます。これはすべての内部サマリー変数をリセットし、指定された UDF_ARGS 引数をグループの内部サマリー値の最初の値として使用します。xxx_reset() は次のように宣言します。

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

    MySQL 5.6 では、xxx_reset() は必要がないか使用されず、UDF インタフェースでは代わりに xxx_clear() が使用されます。ただし、古いバージョンのサーバーで UDF を動作させる場合、xxx_reset()xxx_clear() を両方定義できます。(両方の関数を含める場合、xxx_reset() 関数は、すべての変数をリセットする xxx_clear() を呼び出してから、xxx_add() を呼び出して UDF_ARGS 引数をグループの最初の値として追加することによって、多くの場合内部的に実装できます。)

  • xxx_clear()

    この関数は、MySQL でサマリー結果をリセットする必要がある場合に呼び出されます。これは新しいグループになるたびに最初に呼び出されますが、一致する行のないクエリーの値をリセットするために呼び出されることもあります。xxx_clear() は次のように宣言します。

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

    is_null は、xxx_clear() を呼び出す前に、CHAR(0) を指すように設定されます。

    処理に問題があった場合は、error 引数が指している変数に値を格納できます。error は文字列バッファーでなく単一バイト変数を指しています。

    xxx_clear() は MySQL 5.6 で必要となります。

  • xxx_add()

    この関数は、同じグループに属するすべての行に対して呼び出されます。これは、UDF_ARGS 引数内の値を内部サマリー変数に追加するために使用します。

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

集約 UDF の xxx() 関数は、非集約 UDF と同様に宣言してください。セクション24.3.2.1「単純な関数のための UDF の呼び出しシーケンス」を参照してください。

集約 UDF の場合、MySQL はグループ内のすべての行が処理されたあとに xxx() 関数を呼び出します。通常はここで UDF_ARGS 引数にアクセスすることはなく、内部サマリー変数に基づいて値を返します。

xxx() での戻り値の処理は、非集約 UDF と同様に行います。セクション24.3.2.4「UDF の戻り値およびエラー処理」を参照してください。

xxx_reset() 関数および xxx_add() 関数は、UDF_ARGS 引数を非集約 UDF の関数と同様に処理します。セクション24.3.2.3「UDF 引数の処理」を参照してください。

is_null および error へのポインタ引数は、xxx_reset()xxx_clear()xxx_add()、および xxx() へのすべての呼び出しで同じです。これを使用すると、エラーが発生したこと、または xxx() 関数が NULL を返すかどうかを記憶できます。文字列を *error に格納しないでください。error は文字列バッファーでなく単一バイト変数を指しています。

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

xxx() が戻るときに *is_null または *error が設定されていた場合、MySQL はグループ関数の結果として NULL を返します。

24.3.2.3 UDF 引数の処理

args パラメータは、次に示すメンバーを持つ UDF_ARGS 構造体を指しています。

  • unsigned int arg_count

    引数の数。特定の数の引数を使用して関数を呼び出す必要がある場合は、初期化関数でこの値をチェックします。例:

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

    配列であるその他の UDF_ARGS メンバー値の場合、配列参照はゼロベースです。つまり、0 から args->arg_count − 1 までのインデックス値を使用して配列メンバーを参照します。

  • enum Item_result *arg_type

    各引数の型を格納する配列へのポインタ。指定可能な型の値は、STRING_RESULTINT_RESULTREAL_RESULT、および DECIMAL_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;
    }

    DECIMAL_RESULT 型の引数は文字列として渡されるため、STRING_RESULT 値と同様にこれらを処理する必要があります。

    関数の引数が特定の型であることを要求する代わりに、初期化関数を使用して、arg_type 要素を必要な型に設定できます。これにより、MySQL が xxx() の各呼び出しで引数をそれらの型に強制的に変更します。たとえば、最初の 2 つの引数がそれぞれ文字列および整数になるように強制的に指定するには、xxx_init() で次の操作を実行します。

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

    1.3DECIMAL カラム値などの正確値型の 10 進数引数は、DECIMAL_RESULT 型で渡されます。ただし、値は文字列として渡されます。数値を受け取るには、初期化関数を使用して、引数が REAL_RESULT 値に強制的に変更されるように指定します。

    args->arg_type[2] = REAL_RESULT;
  • char **args

    args->args は、関数に渡される引数の一般的な性質についての情報を初期化関数に通知します。定数引数 i の場合、args->args[i] は引数値を指しています。(値に正しくアクセスする方法については、以降の説明を参照してください。)非定数引数の場合、args->args[i]0 です。定数引数は、34*7-2SIN(3.14) などの定数のみを使用した式です。非定数引数は、行によって変化することがある値を参照する式のことであり、カラム名、非定数引数を指定して呼び出される関数などがあります。

    メイン関数への各呼び出しで、args->args には、現在処理されている行に関して渡される実際の引数が含まれています。

    引数 iNULL の場合、args->args[i] はゼロポインタ (0) です。引数が NULL ではない場合、関数は引数を次のように参照できます。

    • STRING_RESULT 型の引数は、文字列ポインタおよび長さとして指定し、バイナリデータまたは任意の長さのデータを処理できます。文字列の内容は args->args[i] として取得でき、文字列の長さは args->lengths[i] です。文字列がゼロで終わると想定しないでください。

    • INT_RESULT 型の引数の場合は、args->args[i]long long 値にキャストする必要があります。

      long long int_val;
      int_val = *((long long*) args->args[i]);
    • REAL_RESULT 型の引数の場合は、args->args[i]double 値にキャストする必要があります。

      double real_val;
      real_val = *((double*) args->args[i]);
    • DECIMAL_RESULT 型の引数の場合、値は文字列として渡さるため、STRING_RESULT 値と同様に処理されるようにしてください。

    • ROW_RESULT 引数は実装されていません。

  • unsigned long *lengths

    初期化関数の場合、lengths 配列は各引数の最大文字列長を示しています。これらは変更しないでください。メイン関数の各呼び出しで、lengths には、現在処理されている行に関して渡されるすべての文字列引数の実際の長さが含まれています。INT_RESULT 型または REAL_RESULT 型の引数の場合は、lengths には (初期化関数の場合と同様に) 引数の最大長が含まれています。

  • char *maybe_null

    初期化関数において、maybe_null 配列は、各引数について引数値が NULL の場合があるかどうかを示します (ない場合は 0、およびある場合は 1)。

  • char **attributes

    args->attributes は UDF 引数の名前についての情報を通知します。引数 i の場合、属性名は args->attributes[i] の文字列として取得でき、属性の長さは args->attribute_lengths[i] です。文字列がゼロで終わると想定しないでください。

    デフォルトでは、UDF 引数の名前は引数を指定するために使用される式のテキストです。UDF の場合、引数にはオプションの [AS] alias_name 句が含まれていることもあり、この場合の引数名は alias_name です。このため、各引数の attributes 値は、エイリアスが指定されているかどうかに応じて異なります。

    UDF my_udf() が次のように呼び出されたとします。

    SELECT my_udf(expr1, expr2 AS alias1, expr3 alias2);

    この場合、attributes 配列および attribute_lengths 配列の値は次のようになります。

    args->attributes[0] = "expr1"
    args->attribute_lengths[0] = 5
    args->attributes[1] = "alias1"
    args->attribute_lengths[1] = 6
    args->attributes[2] = "alias2"
    args->attribute_lengths[2] = 6
  • unsigned long *attribute_lengths

    attribute_lengths 配列は、各属性名の長さを示しています。

24.3.2.4 UDF の戻り値およびエラー処理

初期化関数は、エラーが発生しなかった場合は 0、およびそうでない場合は 1 を返します。エラーが発生した場合、xxx_init()message パラメータに NULL で終わるエラーメッセージを格納します。メッセージはクライアントに返されます。メッセージバッファーの長さは MYSQL_ERRMSG_SIZE 文字ですが、標準の端末画面の幅にメッセージが収まるように、メッセージを 80 文字未満に維持するようにしてください。

long long 関数および double 関数の場合、メイン関数 xxx() の戻り値は関数値です。文字列関数は結果へのポインタを返し、*length を戻り値の (バイト単位の) 長さに設定します。例:

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

MySQL は、result パラメータを使用してバッファーを xxx() 関数に渡します。このバッファーは 255 文字を保持するための十分な長さがあり、マルチバイト文字も保持できます。xxx() 関数はこのバッファーに結果を格納でき (結果が収まる場合)、その場合は戻り値にそのバッファーへのポインタを設定してください。関数が別のバッファーに結果を格納する場合は、そのバッファーへのポインタを返します。

文字列関数で提供されたバッファーを使用しない場合 (たとえば、255 文字より長い文字列を返す必要がある場合)、xxx_init() 関数または xxx() 関数で malloc() を使用して独自のバッファーの領域を割り当て、xxx_deinit() 関数でその領域を解放する必要があります。以降の xxx() の呼び出しで再利用できるように、割り当てられたメモリーを UDF_INIT 構造体の ptr スロットに格納できます。セクション24.3.2.1「単純な関数のための UDF の呼び出しシーケンス」を参照してください。

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

*is_null = 1;

メイン関数でエラーを返すことを示すには、*error1 に設定します。

*error = 1;

xxx() がいずれかの行で *error1 を設定した場合、XXX() が呼び出されたステートメントによって処理される現在の行および後続の行の関数値は NULL です。(後続の行では xxx() は呼び出されません。)

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

UDF を実装するファイルは、サーバーが実行されるホストでコンパイルおよびインストールする必要があります。MySQL ソース配布に含まれている UDF ファイルの例 sql/udf_example.cc を使用して、このプロセスについて説明します。

スレーブサーバーにレプリケーションされるステートメントで UDF が参照される場合は、すべてのスレーブでその関数が使用可能である必要があります。そうしないと、スレーブでその関数を呼び出そうとしたときに、レプリケーションがスレーブで失敗します。

次の手順は Unix の場合です。Windows での手順は、このセクションの以降で説明します。

udf_example.cc ファイルには次の関数が含まれています。

  • metaphon() は、文字列引数の metaphon 文字列を返します。これは soundex 文字列に似ていますが、英語向けに調整されています。

  • myfunc_double() は、引数内の文字の ASCII 値の合計をその引数の長さの合計で割ったものを返します。

  • myfunc_int() は、その引数の長さの合計を返します。

  • sequence([const int]) は、指定された数値または 1 (数値が指定されなかった場合) から始まるシーケンスを返します。

  • lookup() は、ホスト名の IP アドレスを返します。

  • reverse_lookup() は、IP アドレスに対するホスト名を返します。この関数は、'xxx.xxx.xxx.xxx' という形式の単一の文字列引数または 4 つの数値のいずれかを使用して呼び出すことができます。

  • avgcost() は、平均コストを返します。これは集約関数です。

動的にロード可能なファイルは、次のようなコマンドを使用して、共有可能なオブジェクトファイルとしてコンパイルします。

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

CMakegcc を使用している場合 (MySQL はそのように構成されています) は、より簡単なコマンドで udf_example.so を作成できます。

shell> make udf_example

UDF が含まれている共有オブジェクトをコンパイルしたら、共有オブジェクトをインストールして MySQL に通知する必要があります。gcc を使用して udf_example.cc から共有オブジェクトをコンパイルすると、udf_example.so という名前のファイルが直接生成されます。共有オブジェクトをサーバーのプラグインディレクトリにコピーし、udf_example.so という名前を付けます。このディレクトリは、plugin_dir システム変数の値から取得できます。

一部のシステムでは、ダイナミックリンカーを構成する ldconfig プログラムは、共有オブジェクトの名前が lib で始まっていない場合、共有オブジェクトを認識しません。この場合は、たとえば、ファイル名を udf_example.so から libudf_example.so に名前変更してください。

Windows では、次の手順を使用してユーザー定義関数をコンパイルできます。

  1. MySQL ソース配布を取得します。セクション2.1.3「MySQL の取得方法」を参照してください。

  2. 必要な場合は CMake ビルドユーティリティーを http://www.cmake.org から取得します。(バージョン 2.6 以降が必要となります)。

  3. ソースツリーで sql ディレクトリを参照します。その場所には udf_example.defudf_example.cc という名前のファイルがあります。両方のファイルをこのディレクトリから作業ディレクトリにコピーします。

  4. 次の内容を持つ CMakemakefile (CMakeLists.txt) を作成します。

    PROJECT(udf_example)
    # Path for MySQL include directory
    INCLUDE_DIRECTORIES("c:/mysql/include")
    ADD_DEFINITIONS("-DHAVE_DLOPEN")
    ADD_LIBRARY(udf_example MODULE udf_example.cc udf_example.def)
    TARGET_LINK_LIBRARIES(udf_example wsock32)
  5. VC プロジェクトファイルおよびソリューションファイルを作成します。

    cmake -G "<Generator>"

    cmake --help を呼び出すと、有効なジェネレータのリストが表示されます。

  6. udf_example.dll を作成します。

    devenv udf_example.sln /build Release

共有オブジェクトファイルがインストールされたら、次のステートメントを使用して、新しい関数について mysqld に通知します。使用しているシステムでオブジェクトファイルのサフィクスが .so ではない場合、正しいサフィクスにすべて置き換えます (たとえば、Windows の場合は .dll)。

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 sequence 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 sequence;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 が関数の新しいバージョンを取得するように、新しいバージョンを再コンパイルする場合です。そうしないと、サーバーは古いバージョンを使用し続けます。

アクティブ関数とは、CREATE FUNCTION を使用してロードされていて、DROP FUNCTION を使用して削除されていない関数です。すべてのアクティブ関数は、サーバーが起動するたびにリロードされますが、--skip-grant-tables オプションを指定して mysqld を起動した場合は異なります。この場合は、UDF の初期化がスキップされ、UDF は使用できません。

24.3.2.6 ユーザー定義関数のセキュリティー上の予防措置

MySQL では、ユーザー定義関数の誤用を防ぐためのさまざまな対策が講じられています。

UDF オブジェクトファイルは任意のディレクトリに配置できません。これらはサーバーのプラグインディレクトリに配置する必要があります。このディレクトリは、plugin_dir システム変数の値から取得できます。

CREATE FUNCTION または DROP FUNCTION を使用するには、mysql データベースの INSERT 権限または DELETE 権限をそれぞれ持っている必要があります。これが必要となるのは、これらのステートメントが mysql.func テーブルに対して行の追加および削除を行うためです。

UDF には、メインの xxx() 関数に対応する xxx シンボルのほかに、少なくとも 1 つのシンボルが定義してください。これらの補助シンボルは、xxx_init()xxx_deinit()xxx_reset()xxx_clear()、および xxx_add() 関数に対応します。mysqld では、xxx シンボルのみを持つ UDF をロードできるかどうかを制御する --allow-suspicious-udfs オプションもサポートされています。デフォルトでは、このオプションはオフになっており、正当な UDF が含まれている共有オブジェクトファイル以外から関数がロードされることを防いでいます。xxx シンボルのみが含まれていて、補助シンボルを含めるように再コンパイルできない古い UDF がある場合は、--allow-suspicious-udfs オプションを指定する必要がある場合があります。それ以外の場合は、この機能を有効にしないようにしてください。

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

新しいネイティブ MySQL 関数を追加するには、ここで説明している手順を使用します。この手順では、ソース配布を使用する必要があります。ネイティブ関数をバイナリ配布に追加することはできません。その場合、MySQL ソースコードを変更して、変更されたソースから MySQL をコンパイルする必要があるためです。別のバージョンの MySQL に移行する場合 (たとえば、新しいバージョンがリリースされたとき) は、新しいバージョンを使用してこの手順を繰り返す必要があります。

スレーブサーバーにレプリケートされるステートメントで新しいネイティブ関数が参照される場合は、すべてのスレーブサーバーで関数を使用できるようにする必要があります。そうしないと、スレーブでその関数を呼び出そうとしたときに、レプリケーションがスレーブで失敗します。

新しいネイティブ関数を追加するには、次のステップに従って、sql ディレクトリ内のソースファイルを変更します。

  1. item_create.cc に関数のためのサブクラスを作成します。

    • 関数が固定された数の引数を受け取る場合は、関数が受け取る引数の数 (0、1、2、または 3 個) に応じて、Create_func_arg0Create_func_arg1Create_func_arg2、または Create_func_arg3 のサブクラスをそれぞれ作成します。たとえば、Create_func_uuidCreate_func_absCreate_func_pow、および Create_func_lpad クラスを参照してください。

    • 関数が受け取る引数の数が可変の場合は、Create_native_func のサブクラスを作成します。たとえば、Create_func_concat を参照してください。

  2. SQL ステートメントで関数を参照できる名前を指定するには、次の配列に行を追加することによって item_create.cc に名前を登録します。

    static Native_func_registry func_array[]

    同じ関数に対して複数の名前を登録できます。たとえば、"LCASE" および "LOWER" の行を参照してください。これらは Create_func_lcase のエイリアスです。

  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 など) からオブジェクトを継承する場合は、それらの関数のいずれかを定義するだけで、多くの場合、親オブジェクトによってほかの関数が提供されます。たとえば、Item_str_func クラスは、::str() によって返された値に対して atof() を実行する val() 関数を定義しています。

  5. 関数に決定性がない場合は、関数の結果をキャッシュしないことを示すために、次のステートメントを項目のコンストラクタに含めます。

    current_thd->lex->safe_to_cache_query=0;

    関数に決定性がない状態とは、引数の値が固定されていても、呼び出しごとに異なる結果が返されることがある場合です。

  6. 多くの場合、次のオブジェクト関数も定義します。

    void Item_func_newname::fix_length_and_dec()

    この関数は、指定された引数に基づいて、少なくとも max_length を計算します。max_length は関数が返すことができる最大文字数です。メイン関数が NULL 値を返せない場合は、この関数で maybe_null = 0 も設定します。この関数は、引数の maybe_null 変数をチェックすることによって、関数のいずれかの引数が NULL を返すことがあるかどうかをチェックできます。これを実行する方法の典型的な例については、Item_func_mod::fix_length_and_dec を参照してください。

すべての関数はスレッドセーフである必要があります。言い換えると、関数内で相互排他ロックで保護せずにグローバル変数または静的変数を使用しないでください。

::val()::val_int()、または ::str() から NULL を返す場合は、null_value を 1 に設定して 0 を返します。

::str() オブジェクト関数については、注意すべき追加の考慮事項があります。

  • String *str 引数は、結果を保持するために使用できる文字列バッファーを提供します。(String 型の詳細は、sql_string.h ファイルを参照してください。)

  • ::str() 関数では、結果が保持されている文字列を返すか、結果が NULL の場合は (char*) 0 を返します。

  • 現在のすべての文字列関数は、絶対に必要である場合を除き、メモリーの割り当てを回避しようとします。

24.4 MySQL のデバッグおよび移植

このセクションは、MySQL をほかのオペレーティングシステムに移植する場合に役立ちます。まず、現在サポートされているオペレーティングシステムのリストを確認してください。http://www.mysql.com/support/supportedplatforms/database.html を参照してください。MySQL の新しい移植版を作成した場合は、オラクルにお知らせください。このドキュメントおよび Web サイト (http://www.mysql.com/) にそれを示して、ほかのユーザーに推奨します。

注記

MySQL の新しい移植版を作成した場合は、GPL ライセンスに基づいて自由にコピーおよび配布できますが、MySQL の著作権所有者になるわけではありません。

動作する POSIX スレッドライブラリがサーバーで必要となります。

MySQL をソースからビルドするには、セクション2.9「ソースから MySQL をインストールする」に示されているツール要件をシステムが満たしている必要があります。

重要

IA64 プラットフォームで icc を使用して MySQL 5.6 をビルドしようとしていて、MySQL Cluster のサポートが必要な場合は、まず icc バージョン 9.1.043 以降を使用していることを確認してください。(詳細は、Bug #21875 を参照してください。)

新しい移植版で問題が発生した場合は、MySQL のデバッグを行う必要があることがあります。セクション24.4.1「MySQL サーバーのデバッグ」を参照してください。

注記

mysqld のデバッグを開始する前に、まずテストプログラム mysys/thr_alarm および mysys/thr_lock を使用できるようにします。これにより、スレッドがわずかながらも動作する可能性があります。

24.4.1 MySQL サーバーのデバッグ

MySQL で非常に新しい一部の機能を使用している場合は、--skip-new (安全でない可能性がある新しい機能がすべて無効にされます) を指定して mysqld の実行を試みることができます。セクションB.5.4.2「MySQL が繰り返しクラッシュする場合の対処方法」を参照してください。

mysqld が起動しない場合は、セットアップを妨げている my.cnf ファイルがないことを確認してください。my.cnf の引数をチェックするには mysqld --print-defaults を使用します。mysqld --no-defaults ... を指定して起動するとそれらの引数は使用されません。

mysqld が CPU やメモリーを使い尽くしてしまうようになった場合、またはハングアップする場合は、mysqladmin processlist status を使用すると、ユーザーが長時間かかるクエリーを実行しているかどうかを確認できます。パフォーマンスの問題、または新しいクライアントが接続できないことに関する問題がある場合は、ほかのウィンドウで mysqladmin -i10 processlist status を実行すると役に立つことがあります。

mysqladmin debug コマンドは、使用されているロック、使用されているメモリー、およびクエリーの使用状況に関する情報を MySQL ログファイルにダンプします。これは一部の問題の解決に役立つことがあります。また、このコマンドでは、MySQL をデバッグ用にまだコンパイルしていなくても、役立つ情報が提供されます。

一部のテーブルが徐々に遅くなるという問題がある場合は、OPTIMIZE TABLE または myisamchk を使用してテーブルを最適化することを試みてください。第5章「MySQL サーバーの管理 を参照してください。また、遅いクエリーを EXPLAIN でチェックしてください。

また、使用している環境に特有である可能性がある問題については、このマニュアルの OS 固有のセクションをお読みください。セクション2.1「一般的なインストールガイド」を参照してください。

24.4.1.1 デバッグのための MySQL のコンパイル

非常に特定された問題がある場合は、MySQL のデバッグを試みることができます。これを実行するには、-DWITH_DEBUG=1 オプションを指定して MySQL を構成する必要があります。mysqld --help を実行すると、MySQL がデバッグ付きでコンパイルされたかどうかを確認できます。--debug フラグがオプションとともに示される場合は、デバッグが有効にされています。この場合は、mysqladmin ver でも mysqld バージョンが mysql ... --debug として示されます。

mysqld-DWITH_DEBUG=1 CMake オプションを指定して構成するとクラッシュして停止する場合は、MySQL 内のコンパイラのバグまたはタイミングのバグが検出された可能性があります。この場合は、-DWITH_DEBUG=1 を使用せずに、CMAKE_C_FLAGS および CMAKE_CXX_FLAGS CMake オプションを使用して -g の追加を試みることができます。mysqld が異常終了した場合は、少なくとも gdb を使用してそれに接続するか、コアファイルに対して gdb を使用して、発生した事象を確認できます。

MySQL をデバッグ用に構成すると、mysqld のヘルスをモニターする多くの追加の安全チェック機能が自動的に有効になります。予期しない現象が検出された場合、stderr にエントリが書き込まれ、これは mysqld_safe によってエラーログに送られます。また、これは、ソース配布を使用していて、MySQL に予期しない問題があった場合、最初に行うことは MySQL をデバッグ用に構成することであることを意味します。(2 番目にすることは、MySQL のメーリングリストにメールを送信して支援を求めることです。セクション1.6.1「MySQL メーリングリスト」 を参照してください。バグを見つけたと思われる場合は、セクション1.7「質問またはバグをレポートする方法」の手順を使用してください。

Windows の MySQL 配布では、mysqld.exe はデフォルトでトレースファイルをサポートするようにコンパイルされています。

24.4.1.2 トレースファイルの作成

mysqld サーバーが起動しない場合、またはサーバーがすぐにクラッシュしてしまう場合は、問題を見つけるためにトレースファイルの作成を試みることができます。

これを行うには、デバッグサポート付きでコンパイルされている mysqld がある必要があります。これは mysqld -V を実行することによって確認できます。バージョン番号が -debug で終わっている場合は、トレースファイルのサポート付きでコンパイルされています。(Windows の場合、MySQL 4.1 以降では、デバッグサーバーの名前は mysqld ではなく mysqld-debug です。)

Unix の場合は /tmp/mysqld.trace、Windows の場合は \mysqld.trace にあるトレースログを使用して、mysqld サーバーを起動します。

shell> mysqld --debug

Windows では、mysqld をサービスとして起動しないようにするために、--standalone フラグも使用してください。コンソールウィンドウで、次のコマンドを使用します。

C:\> mysqld-debug --debug --standalone

このあと、2 つ目のコンソールウィンドウで mysql.exe コマンド行ツールを使用して、問題を再現できます。mysqld サーバーを停止するには、mysqladmin shutdown を使用します。

トレースファイルは非常に大きくなることがあります。より小さいトレースファイルが生成されるようにするには、次のようなデバッグオプションを使用できます。

mysqld --debug=d,info,error,query,general,where:O,/tmp/mysqld.trace

これにより、もっとも関心があるタグの情報のみがトレースファイルに出力されます。

これに関するバグレポートを作成する場合は、動作に問題があると思われるトレースファイルの行のみを、該当するメーリングリストに送信してください。問題のある場所を見つけることができない場合は、バグレポートを開いて、MySQL の開発者が調査できるように、トレースファイルをレポートにアップロードしてください。手順については、セクション1.7「質問またはバグをレポートする方法」を参照してください。

トレースファイルは、Fred Fish が作成した DBUG パッケージによって生成されます。セクション24.4.3「DBUG パッケージ」を参照してください。

24.4.1.3 pdb を使用した Windows のクラッシュダンプの作成

プログラムデータベースファイル (拡張子は pdb) は、MySQL の非インストール配布に含まれています。これらのファイルには、問題が発生したときに MySQL インストール環境をデバッグするための情報が含まれています。

PDB ファイルには、より詳細なトレースファイルおよびダンプファイルを作成できる、mysqld およびその他のツールについての詳細な情報が含まれています。これらをワトソン博士、WinDbg、および Visual Studio とともに使用して、mysqld をデバッグできます。

PDB ファイルについては、Microsoft サポート技術情報の記事 121366 を参照してください。使用可能なデバッグオプションについては、Windows のデバッグツールを参照してください。

ワトソン博士はすべての Windows 配布でインストールされますが、Windows 開発ツールをインストールした場合、ワトソン博士は Visual Studio に付属しているデバッガである WinDbg、または Borland や Delphi で提供されているデバッグツールに置き換えられている可能性があります。

ワトソン博士を使用してクラッシュファイルを生成するには、次の手順に従います。

  1. -i オプションを使用して drwtsn32.exe を対話的に実行することによって、ワトソン博士を起動します。

    C:\> drwtsn32 -i
  2. 「ログ ファイルのパス」にトレースファイルを格納するディレクトリを設定します。

  3. 「すべてのスレッド コンテキストをダンプ」および「既存のログ ファイルに追加する」が選択されていることを確認します。

  4. 「ダンプ シンボル テーブル」「メッセージ ボックスによる通知」「音による通知」、および「クラッシュ ダンプ ファイルの作成」のチェックを外します。

  5. 「インストラクションの数」に、スタックトレース内の呼び出しを必要なだけ取得できる値を設定します。値は 25 にすれば十分です。

生成されるファイルは、非常に大きくなる可能性があります。

24.4.1.4 gdb での mysqld のデバッグ

ほとんどのシステムでは、mysqld がクラッシュした場合に詳細な情報を取得するために、gdb からも mysqld を起動できます。

Linux の一部の古い gdb バージョンでは、mysqld のスレッドをデバッグできるようにするには、run --one-thread を使用する必要があります。この場合、一度にアクティブにできるのは 1 つのスレッドのみです。スレッドのデバッグは gdb 5.1 のほうがより良く機能するため、このバージョンにアップグレードすると最適です。

gdbmysqld を実行すると、NPTL スレッド (Linux の新しいスレッドライブラリ) に起因する問題が発生することがあります。次のような現象が発生します。

  • mysqld が起動中 (「接続準備完了」と出力される前) にハングアップする。

  • mysqldpthread_mutex_lock() または pthread_mutex_unlock() の呼び出し中にクラッシュする。

この場合、gdb を起動する前に、シェルで次の環境変数を設定してください。

LD_ASSUME_KERNEL=2.4.1
export LD_ASSUME_KERNEL

gdbmysqld を実行するときは、--skip-stack-trace を使用してスタックトレースを無効にし、gdb 内でセグメンテーション違反を捕捉できるようにする必要があります。

MySQL 4.0.14 以降では、mysqld--gdb オプションを使用してください。これにより、SIGINT 用の割り込みハンドラ (mysqld^C で停止してブレークポイントを設定するために必要となります) がインストールされ、スタックトレースおよびコアファイル処理が無効になります。

新しい接続が常時多数発生する場合、gdb は古いスレッドのメモリーを解放しないため、gdb で MySQL をデバッグすることは非常に困難です。この問題を回避するには、thread_cache_sizemax_connections + 1 に等しい値に設定して mysqld を起動します。ほとんどの場合、--thread_cache_size=5' を使用するだけでもかなり改善されます。

SIGSEGV シグナルが発生して mysqld が異常終了したときに Linux でコアダンプを取得する場合は、--core-file オプションを指定して mysqld を起動します。このコアファイルは、mysqld が異常終了した理由を見つけるために役立つことがあるバックトレースを作成するために使用できます。

shell> gdb mysqld coregdb> backtrace full
gdb> quit

セクションB.5.4.2「MySQL が繰り返しクラッシュする場合の対処方法」を参照してください。

Linux で gdb 4.17.x 以降を使用している場合は、次の情報を持つ .gdb ファイルを現在のディレクトリにインストールしてください。

set print sevenbit off
handle SIGUSR1 nostop noprint
handle SIGUSR2 nostop noprint
handle SIGWAITING nostop noprint
handle SIGLWP nostop noprint
handle SIGPIPE nostop
handle SIGALRM nostop
handle SIGHUP nostop
handle SIGTERM nostop noprint

gdb を使用したスレッドのデバッグに問題がある場合、gdb 5.x をダウンロードし、これを代わりに試してみてください。新しいバージョンの gdb ではスレッド処理が非常に改善されています。

mysqld をデバッグする方法の例を次に示します。

shell> gdb /usr/local/libexec/mysqldgdb> run
...
backtrace full # Do this when mysqld crashes

上記の出力をバグレポートに含め、セクション1.7「質問またはバグをレポートする方法」の手順を使用してバグレポートを提出できます。

mysqld がハングアップした場合は、strace/usr/proc/bin/pstack などのシステムツールを使用して、mysqld がハングアップした場所を調査できます。

strace /tmp/log libexec/mysqld

Perl の DBI インタフェースを使用している場合は、trace メソッドを使用するか、DBI_TRACE 環境変数を設定することによって、デバッグ情報をオンにできます。

24.4.1.5 スタックトレースの使用

オペレーティングシステムによっては、mysqld が予期せずに異常終了した場合に、エラーログにスタックトレースが含まれています。これを使用して、mysqld が異常終了した場所 (および多くの場合その理由) を見つけることができます。セクション5.2.2「エラーログ」 を参照してください。スタックトレースを取得するには、-fomit-frame-pointer オプションを gcc に指定して mysqld をコンパイルしないでください。セクション24.4.1.1「デバッグのための MySQL のコンパイル」を参照してください。

エラーログのスタックトレースは次のように出力されます。

mysqld got signal 11;
Attempting backtrace. You can use the following information
to find out where mysqld died. If you see no messages after
this, something went terribly wrong...
stack_bottom = 0x41fd0110 thread_stack 0x40000
mysqld(my_print_stacktrace+0x32)[0x9da402]
mysqld(handle_segfault+0x28a)[0x6648e9]
/lib/libpthread.so.0[0x7f1a5af000f0]
/lib/libc.so.6(strcmp+0x2)[0x7f1a5a10f0f2]
mysqld(_Z21check_change_passwordP3THDPKcS2_Pcj+0x7c)[0x7412cb]
mysqld(_ZN16set_var_password5checkEP3THD+0xd0)[0x688354]
mysqld(_Z17sql_set_variablesP3THDP4ListI12set_var_baseE+0x68)[0x688494]
mysqld(_Z21mysql_execute_commandP3THD+0x41a0)[0x67a170]
mysqld(_Z11mysql_parseP3THDPKcjPS2_+0x282)[0x67f0ad]
mysqld(_Z16dispatch_command19enum_server_commandP3THDPcj+0xbb7[0x67fdf8]
mysqld(_Z10do_commandP3THD+0x24d)[0x6811b6]
mysqld(handle_one_connection+0x11c)[0x66e05e]

トレースの関数名の解決に失敗した場合、トレースに格納される情報が少なくなります。

mysqld got signal 11;
Attempting backtrace. You can use the following information
to find out where mysqld died. If you see no messages after
this, something went terribly wrong...
stack_bottom = 0x41fd0110 thread_stack 0x40000
[0x9da402]
[0x6648e9]
[0x7f1a5af000f0]
[0x7f1a5a10f0f2]
[0x7412cb]
[0x688354]
[0x688494]
[0x67a170]
[0x67f0ad]
[0x67fdf8]
[0x6811b6]
[0x66e05e]

後者の場合は、resolve_stack_dump ユーティリティーを使用して、次の手順を使用することによって、mysqld が異常終了した場所を判別できます。

  1. スタックトレースから mysqld.stack などのファイルに数値をコピーします。この数値には、囲んでいた角括弧を含めないでください。

    0x9da402
    0x6648e9
    0x7f1a5af000f0
    0x7f1a5a10f0f2
    0x7412cb
    0x688354
    0x688494
    0x67a170
    0x67f0ad
    0x67fdf8
    0x6811b6
    0x66e05e
  2. mysqld サーバーのシンボルファイルを作成します。

    shell> nm -n libexec/mysqld > /tmp/mysqld.sym

    mysqld が静的にリンクされていない場合は、代わりに次のコマンドを使用します。

    shell> nm -D -n libexec/mysqld > /tmp/mysqld.sym

    C++ のシンボルをデコードする場合は、nm に対して --demangle を使用します (使用できる場合)。使用しているバージョンの nm にこのオプションがない場合は、スタックダンプが生成されたあとに c++filt コマンドを使用して、C++ 名をデマングルする必要があります。

  3. 次のコマンドを実行します。

    shell> resolve_stack_dump -s /tmp/mysqld.sym -n mysqld.stack

    デマングルされた C++ 名をシンボルファイルに含めることができなかった場合は、c++filt を使用して resolve_stack_dump の出力を処理します。

    shell> resolve_stack_dump -s /tmp/mysqld.sym -n mysqld.stack | c++filt

    これにより、mysqld が異常終了した場所が出力されます。この出力が、mysqld が異常終了した理由を見つけるために役立たない場合は、バグレポートを作成して、上記のコマンドの出力をバグレポートに含めてください。

    ただし、多くの場合、スタックトレースだけがあっても問題の原因を見つけるために役立ちません。バグを見つけたり回避策を提供したりするためには、ほとんどの場合、mysqld が強制終了されたステートメントを知る必要があり、問題を再現できるテストケースがあれば役に立ちます。セクション1.7「質問またはバグをレポートする方法」を参照してください。

新しいバージョンの glibc スタックトレース関数では、オブジェクトへの相対アドレスも出力されます。glibc ベースのシステム (Linux) では、プラグイン内でのクラッシュのトレースは次のようになります。

plugin/auth/auth_test_plugin.so(+0x9a6)[0x7ff4d11c29a6]

相対アドレス (+0x9a6) をファイル名および行番号に変換するには、次のコマンドを使用します。

shell> addr2line -fie auth_test_plugin.so 0x9a6auth_test_plugin
mysql-trunk/plugin/auth/test_plugin.c:65

addr2line ユーティリティーは Linux の binutils パッケージの一部です。

Solaris でも手順は同様です。Solaris の printstack() では、相対アドレスがすでに出力されています。

plugin/auth/auth_test_plugin.so:0x1510

これを変換するには、次のコマンドを使用します。

shell> gaddr2line -fie auth_test_plugin.so 0x1510mysql-trunk/plugin/auth/test_plugin.c:88

Windows では、アドレス、関数名、および行がすでに出力されています。

000007FEF07E10A4 auth_test_plugin.dll!auth_test_plugin()[test_plugin.c:72]

24.4.1.6 mysqld でのエラーの原因を見つけるためのサーバーログの使用

一般クエリーログを有効にして mysqld を起動する前に、myisamchk を使用してすべてのテーブルをチェックしてください。第5章「MySQL サーバーの管理 を参照してください。

mysqld が異常終了またはハングアップする場合は、一般クエリーログを有効にして mysqld を起動してください。セクション5.2.3「一般クエリーログ」を参照してください。mysqld がふたたび異常終了したら、ログファイルの最後の部分を調査して、mysqld が強制終了されたクエリーを見つけることができます。

デフォルトの一般クエリーログファイルを使用した場合、ログはデータベースディレクトリに host_name.log として格納されます。ほとんどの場合、mysqld が強制終了されたのはログファイル内の最後のクエリーですが、可能であれば、mysqld を再起動して、見つかったクエリーを mysql コマンド行ツールから実行することによって、このことを検証してください。これが動作する場合は、完了しなかった複雑なクエリーもすべてテストしてください。

また、長い時間がかかるすべての SELECT ステートメントに対して EXPLAIN コマンドを試すことで、mysqld がインデックスを適切に使用していることを確認できます。セクション13.8.2「EXPLAIN 構文」を参照してください。

実行に長い時間がかかるクエリーを見つけるには、スロークエリーログを有効にして mysqld を起動します。セクション5.2.5「スロークエリーログ」を参照してください。

エラーログファイル (通常は hostname.err という名前) に mysqld restarted というテキストがあった場合は、mysqld でエラーが発生した原因であるクエリーが見つかった可能性があります。これが発生した場合、myisamchk を使用してすべてのテーブルをチェックし (第5章「MySQL サーバーの管理を参照してください)、MySQL ログファイル内のクエリーをテストして、失敗するかどうかを確認します。そのようなクエリーが見つかった場合は、まず最新バージョンの MySQL にアップグレードすることを試してください。これで解決されず、mysql のメールアーカイブで参考になる回答が見つからない場合は、MySQL メーリングリストにバグを報告してください。メーリングリストについては http://lists.mysql.com/ で説明されており、アーカイブのオンラインリストへのリンクもあります。

--myisam-recover-options を指定して mysqld を起動した場合、MySQL は「not closed properly」または「crashed」としてマークされている MyISAM テーブルを自動的にチェックして修復しようとします。これが発生した場合、MySQL は hostname.err ファイルに「警告: テーブル ... をチェックしています」と書き込み、テーブルを修復する必要がある場合は、「警告: テーブルを修復しています」がそのあとに書き込まれます。これらのエラーを多数受け取り、その直前に予期しない mysqld の停止がなかった場合は、何らかの問題があるため、さらに調査する必要があります。セクション5.1.3「サーバーコマンドオプション」を参照してください。

MySQL 5.6 では、サーバーが MyISAM テーブルの破損を検出すると、追加の情報 (ソースファイルの名前と行番号、テーブルにアクセスしていたスレッドのリストなど) をエラーログに書き込みます。たとえば、「thread_id=1 からエラーを受け取りました。mi_dynrec.c:368」です。これは、バグレポートに含めると役に立つ情報です。

mysqld が予期せず異常終了することは良い兆候ではありませんが、この場合は Checking table... メッセージを調査するのではなく、mysqld が異常終了した原因を見つけるようにしてください。

24.4.1.7 テーブルが破損した場合のテストケースの作成

テーブルが破損した場合、または一部の更新コマンドのあとに mysqld で常に障害が発生する場合は、次の手順を行うことによってこのバグが再現可能かどうかをテストできます。

  • MySQL デーモンを停止します (mysqladmin shutdown を使用します)。

  • テーブルのバックアップを作成します (非常にまれですが、修復によって何らかの障害が発生する場合に対して保護するため)。

  • myisamchk -s database/*.MYI を使用してすべてのテーブルをチェックします。不正なテーブルがあった場合は、myisamchk -r database/table.MYI を使用して修復します。

  • テーブルの 2 番目のバックアップを作成します。

  • より多くの領域が必要な場合は、MySQL データディレクトリから古いログファイルを削除 (または移動) します。

  • バイナリログを有効にして mysqld を起動します。mysqld がクラッシュするクエリーを見つける場合は、一般クエリーログも有効にしてサーバーを起動します。セクション5.2.3「一般クエリーログ」およびセクション5.2.4「バイナリログ」を参照してください。

  • テーブルがクラッシュしたら、mysqld サーバーを停止します。

  • バックアップをリストアします。

  • バイナリログを有効にせずにmysqld サーバーを再起動します。

  • mysqlbinlog binary-log-file | mysql を指定してコマンドを再実行します。バイナリログは、hostname-bin.NNNNNN という名前で MySQL データベースディレクトリに保存されます。

  • テーブルがふたたび破損したか、上記のコマンドで mysqld が異常終了する場合は、簡単に修正できる可能性がある再現可能なバグが見つかりました。セクション1.7「質問またはバグをレポートする方法」の手順を使用して、テーブルおよびバイナリログをバグデータベースに FTP で送信してください。サポートのお客様の場合は、MySQL カスタマサポートセンター (http://www.mysql.com/support/) を使用して MySQL チームにその問題を通知し、可能な限り早く修正してもらうことができます。

24.4.2 MySQL クライアントのデバッグ

統合デバッグパッケージを使用して MySQL クライアントをデバッグできるようにするには、-DWITH_DEBUG=1 を指定して MySQL を構成します。セクション2.9.4「MySQL ソース構成オプション」を参照してください。

クライアントを実行する前に、MYSQL_DEBUG 環境変数を設定します。

shell> MYSQL_DEBUG=d:t:O,/tmp/client.traceshell> export MYSQL_DEBUG

これにより、クライアントは /tmp/client.trace にトレースファイルを生成します。

独自のクライアントコードに問題がある場合は、動作することがわかっているクライアントを使用してサーバーに接続し、クエリーを実行してください。これを行うには、mysql をデバッグモードで実行します (デバッグを有効にして MySQL をコンパイルしたことを想定しています)。

shell> mysql --debug=d:t:O,/tmp/client.trace

これにより、バグレポートをメール送信するときに役立つ情報が得られます。セクション1.7「質問またはバグをレポートする方法」を参照してください。

クライアントが「正しい」ように見えるコードでクラッシュしている場合は、mysql.h インクルードファイルが MySQL のライブラリファイルと一致していることを確認してください。非常によくある間違いは、古い MySQL インストール環境にある古い mysql.h ファイルを新しい MySQL ライブラリとともに使用していることです。

24.4.3 DBUG パッケージ

MySQL サーバーおよびほとんどの MySQL クライアントは、もともと Fred Fish によって作成された DBUG パッケージとともにコンパイルされます。MySQL をデバッグ用に構成した場合は、このパッケージによって、プログラムが実行している内容のトレースファイルを取得できるようになります。セクション24.4.1.2「トレースファイルの作成」を参照してください。

このセクションでは、デバッグサポート付きでビルドされた MySQL プログラムのコマンド行のデバッグオプションに指定できる引数値をまとめています。DBUG パッケージを使用したプログラミングについては、MySQL ソース配布の dbug ディレクトリにある DBUG マニュアルを参照してください。最新の DBUG マニュアルを入手するには、最新の配布を使用してください。

DBUG パッケージは、--debug[=debug_options] または -# [debug_options] オプションを指定してプログラムを起動することによって使用できます。--debug または -# オプションを指定して、debug_options 値を指定しない場合、ほとんどの MySQL プログラムではデフォルト値が使用されます。サーバーのデフォルトは、Unix の場合は d:t:i:o,/tmp/mysqld.trace、Windows の場合は d:t:i:O,\mysqld.trace です。このデフォルトには次のような効果があります。

  • d: すべてのデバッグマクロの出力を有効にします

  • t: 関数の呼び出しおよび終了をトレースします

  • i: 出力行に PID を追加します

  • o,/tmp/mysqld.traceO,\mysqld.trace: デバッグ出力ファイルを設定します

ほとんどのクライアントプログラムでは、プラットフォームにかかわらず、デフォルトの debug_options 値である d:t:o,/tmp/program_name.trace が使用されます。

シェルのコマンド行で指定されることがある、デバッグ制御文字列のいくつかの例を次に示します。

--debug=d:t
--debug=d:f,main,subr1:F:L:t,20
--debug=d,input,output,files:n
--debug=d:t:i:O,\\mysqld.trace

mysqld の場合は、debug システム変数を設定することによって、DBUG 設定を実行時に変更することもできます。この変数にはグローバル値とセッション値があります。

mysql> SET GLOBAL debug = 'debug_options';mysql> SET SESSION debug = 'debug_options';

実行時に変更するには、セッション値であっても SUPER 権限が必要となります。

debug_options 値は、コロンで区切られた一連のフィールドです。

field_1:field_2:...:field_N

この値内の各フィールドは必須のフラグ文字で構成され、フラグ文字の前に + 文字または - 文字、およびフラグ文字の後ろにカンマ区切りの修飾子のリストがオプションで付加されることがあります。

[+|-]flag[,modifier,modifier,...,modifier]

次の表は、許可されるフラグ文字を示しています。認識されないフラグ文字は暗黙のうちに無視されます。

フラグ

説明

d

DBUG_XXX マクロからの現在の状態に関する出力を有効にします。キーワードのリストがあとに続くことがあり、そのキーワードを使用する DBUG マクロの出力のみが有効になります。キーワードのリストが空の場合は、すべてのマクロの出力が有効になります。

MySQL では、一般的に有効にされるデバッグマクロのキーワードは、enterexiterrorwarninginfo、および loop です。

D

各デバッガの出力行のあとに待機します。引数は 0.1 秒単位の待機時間であり、マシンの能力の影響を受けます。たとえば、D,20 は 2 秒の待機を指定します。

f

デバッグ、トレース、およびプロファイリングの対象を指定された関数のリストに制限します。空のリストの場合はすべての関数が有効になります。適切な d フラグまたは t フラグを指定する必要があり、それらのフラグが有効な場合にのみ、このフラグはそれらのフラグのアクションを制限します。

F

デバッグ出力またはトレース出力の各行にソースファイル名を示します。

i

デバッグ出力またはトレース出力の各行に PID またはスレッド ID でプロセスを示します。

L

デバッグ出力またはトレース出力の各行にソースファイルの行番号を示します。

n

デバッグ出力またはトレース出力の各行に現在の関数のネストの深さを出力します。

N

デバッグ出力の各行に番号を付けます。

o

デバッガの出力ストリームを指定されたファイルにリダイレクトします。デフォルトの出力先は stderr です。

O

o と似ていますが、ファイルは書き込みごとに実際にはフラッシュされます。必要な場合、ファイルが書き込みごとに閉じられてふたたび開きます。

p

デバッガアクションを指定されたプロセスに限定します。デバッガアクションが実行されるためには、プロセスが DBUG_PROCESS マクロで識別され、リスト内のプロセスと一致する必要があります。

P

デバッグ出力またはトレース出力の各行に現在のプロセス名を出力します。

r

新しい状態をプッシュするときに、前の状態の関数のネストレベルを継承しません。出力を左マージンから開始する場合に便利です。

S

_sanity() から 0 以外が返されるまで、デバッグされる各関数で関数 _sanity(_file_,_line_) を実行します。

t

関数の呼び出し/終了のトレース行を有効にします。最大のトレースレベルを示す数値を指定するリスト (修飾子が 1 つだけ含まれています) があとに続く場合があり、それを超えるとデバッグマクロまたはトレースマクロの出力は行われません。デフォルトはコンパイル時のオプションです。

フラグの前の + 文字や - 文字、およびフラグの後ろに続く修飾子のリストは、df などのフラグ文字に対して使用して、該当するすべての修飾子または一部の修飾子に対してデバッグ操作を有効にできます。

  • フラグの前に + または - がない場合、フラグ値は指定された修飾子リストのとおりに設定されます。

  • フラグの前に + または - がある場合は、リスト内の修飾子が現在の修飾子リストに対して追加または削除されます。

次の例は、d フラグでのこの動作を示しています。d のリストが空の場合は、すべてのデバッグマクロの出力が有効になります。リストが空でない場合は、リスト内のマクロキーワードの出力のみが有効になります。

次のステートメントでは、指定されたとおりに d 値が修飾子リストに設定されます。

mysql> SET debug = 'd';mysql> SELECT @@debug;+---------+
| @@debug |
+---------+
| d |
+---------+
mysql> SET debug = 'd,error,warning';mysql> SELECT @@debug;+-----------------+
| @@debug |
+-----------------+
| d,error,warning |
+-----------------+

フラグの前の + または - は、現在の d 値に対して追加または削除を行います。

mysql> SET debug = '+d,loop';mysql> SELECT @@debug;+----------------------+
| @@debug |
+----------------------+
| d,error,warning,loop |
+----------------------+
mysql> SET debug = '-d,error,loop';mysql> SELECT @@debug;+-----------+
| @@debug |
+-----------+
| d,warning |
+-----------+

すべてのマクロが有効な状態に対して追加した場合は、何も変更されません。

mysql> SET debug = 'd';mysql> SELECT @@debug;+---------+
| @@debug |
+---------+
| d |
+---------+
mysql> SET debug = '+d,loop';mysql> SELECT @@debug;+---------+
| @@debug |
+---------+
| d |
+---------+

有効なすべてのマクロを無効にすると、d フラグは完全に無効になります。

mysql> SET debug = 'd,error,loop';mysql> SELECT @@debug;+--------------+
| @@debug |
+--------------+
| d,error,loop |
+--------------+
mysql> SET debug = '-d,error,loop';mysql> SELECT @@debug;+---------+
| @@debug |
+---------+
| |
+---------+
注記

MySQL 5.6.12 より前では、+ および - の修飾子が正しく処理されない場合があり、フラグ値が不正な状態のままになる可能性があります。debug の設定順序を事前に確認するか、+ または - を使用せずに設定してください。

関連キーワード:  plugin,サーバー,MYSQL,ディスクリプタ,セクション,mysqld,simple,PLUGIN,ライブラリ,タイプ