Skip to main content
プレビュー機能は、APIが最終化される前に拡張機能に公開されるサーバー機能です。プレビュー機能を宣言する拡張機能は、vsql_allow_preview_extensions = ON が設定されている場合にのみインストールできます(プレビュー層の有効化を参照)—プレビュー機能を使用しない拡張機能は、この設定に関係なく通常通りインストールされます。
プレビュー機能APIは安定していません。プレビュー機能に依存して構築された拡張機能は、サーバーのアップデート後にロードに失敗する可能性があります。機能が安定化すると、そのヘッダーはバージョン化された安定版 C++ SDK パスに移動されます。

プレビュー層の有効化

プレビュー機能を使用する拡張機能をインストールする前に、SET PERSIST を使用して vsql_allow_preview_extensions = ON を設定します:
SET GLOBAL はこの変数に対して拒否されます — サーバーは設定を再起動後に保持するために SET PERSIST を要求します。プレビュー機能を持つ拡張機能は起動時にロードされるため、サーバーが起動する時点でこの変数が ON である必要があります。 mysqld を直接起動する場合(例:サーバーを初めて起動するインストールスクリプトから)、コマンドライン引数でフラグを渡してください — mysqld-auto.cnf がまだ存在しないため、永続化された値を保持できません:
無効化するには:
プレビュー機能を使用する拡張機能が現在インストールされている場合、失敗します。まずそれらの拡張機能をアンインストールし、その後設定をオフにします。

機能インデックス

登録パターン

プレビュー機能を使用するには、ファイルスコープで機能オブジェクトを値として宣言し、make_extension() 内の .with() に参照で渡します。サーバーは登録時にオブジェクトの abi ポインタを設定します:
.with(capability) はサーバーに拡張機能が必要とする機能を伝えます。拡張機能をインストールする際に vsql_allow_preview_extensions が OFF の場合、サーバーは拡張機能名を示すエラーを返してインストールを拒否します:ERROR 3219 (HY000): Failed to load VEF extension 'name': extension requires preview capabilities but vsql_allow_preview_extensions is OFF。このメッセージは、どの機能が原因であったかは示しません。
拡張機能内で宣言されたすべての機能オブジェクトは、.with() に正確に1回渡す必要があります。ロード時にサーバーは宣言されたすべての機能インスタンスを .with() が受け取った内容と照合し、ルール違反の場合 INSTALL EXTENSION を失敗させます:
  • 宣言されたが .with() に渡されなかった場合: capability '<Type>' was declared but never passed to .with(); every CapabilityBase-derived static must be registered via .with(cap) in the extension builder
  • 同じインスタンスを .with() に複数回渡した場合: capability '<Type>' passed to .with() more than once
  • .with() に渡されたオブジェクトが機能でない場合: .with() received an object that does not inherit vsql::detail::CapabilityBase; not a registered capability
完全なエラーは Failed to load VEF extension '<name>': vef_register returned an error: <message above> として表示されます。

Keyring アクセス

keyring 機能(vsql::preview::keyring)は、MySQL keyring コンポーネントに格納されたシークレットを拡張機能が読み書きできるようにします。APIキー、暗号化キー、またはSQLテーブルに保存したくないその他のシークレットに使用します。 機能名 VEF_PREVIEW_KEYRING_NAME"vsql::preview::keyring" です。 読み書きが成功するには、MySQLサーバーに keyring コンポーネントがインストールされている必要があります。インストールされていない場合、操作は KeyringCapability::Status::UNAVAILABLE を返します。

ステータス値

KeyringCapability::Statusread()ReadResult 内)および write() から返されるスコープ付き列挙型です:

機能の宣言

ヘッダーを含め、ファイルスコープで機能オブジェクトを宣言し、.with() に渡します:
g_keyring オブジェクトはロード時にサーバーによって初期化されます。keyring コンポーネントがインストールされていない場合、read() および write() は実行時に Status::UNAVAILABLE を返します — 別の利用可能性チェックで処理をガードする代わりに、各呼び出しでステータスを確認してください。

読み書き

data_id はキー識別子です。auth_id は所有ユーザーです — 内部キー(特定のユーザーに関連付けられていない)を読み書きするには空文字列(または read では省略、デフォルトは {})を渡します。 read は値で ReadResult を返します。構造化バインディングでバインドします:
Status::OK 以外のステータスの場合、value は空です。 write は直接 Status を返し、datadata_id / auth_id で格納します。

完全な例

これはサーバーの villagesql/test-extensions/ ツリーにある vsql_keyring_reader テスト拡張機能の簡略化されたバージョンです。2つの VDF を登録します:keyring_read および keyring_store

MySQL サービス

mysql_services 機能(vsql::preview::mysql_services)は、拡張機能が MySQL レジストリサービスを利用できるようにします — MySQL コンポーネントが利用するのと同じサービスで、インストールされたコンポーネントまたはサーバーコアによって提供されます。拡張機能は必要なすべてのサービスを1か所で宣言します。サーバーは拡張機能のロード時に各サービスを取得し、拡張機能のアンロード時に解放します。 機能名 VEF_PREVIEW_MYSQL_SERVICES_NAME"vsql::preview::mysql_services" です。 サーバーの機構に専用の VEF 機能がない場合にこれを使用します。セッション属性と keyring 自身のコンポーネントサービスは、どちらもこの方法で利用できます。サポートされるのは利用のみです。拡張機能自身の実装をレジストリに登録することは今後の作業として計画されており、この機能には含まれません。

機能の宣言

ファイルスコープで MysqlServices オブジェクトを1つ宣言し、利用する各サービスを VSQL_REQUIRE_SERVICE で指定し、そのオブジェクトを .with() に渡します。各サービスについて MySQL 自身のヘッダーを含めてください — サービスの型とメソッドはそのヘッダーで宣言されています:
VSQL_REQUIRE_SERVICE(services, name, var) は、サーバーが取得したサービスを書き込む参照 var を宣言し、nameservices に登録します。var は自動的に static として宣言されます。MysqlServices オブジェクトも static である必要があり、手動で宣言する参照も同様です。サーバーはロード時にそれらを介して書き込むため、それらは拡張機能より長く存続する必要があります。

特定の実装の指定

VSQL_REQUIRE_SERVICEname を2回使用します — C++ の SERVICE_TYPE(name) として、およびサーバーがレジストリで検索する文字列として。この修飾なしの名前では、サーバーはサービスのデフォルト実装を取得します。 代わりに1つの実装を指定するには、修飾されたレジストリ名 — MySQL の PROVIDES_SERVICE(component, service) が生成する service.component の形式 — を使用します。以下は、デフォルトではなく component_keyring_file コンポーネントの keyring リーダーを要求します:
修飾された名前は修飾なしの名前と同じ方法で取得されるため、通常のルールが適用されます。その実装が登録されていない場合、拡張機能は別の実装にフォールバックするのではなくインストールに失敗します。

MySQL のヘッダーに対するビルド

サービス定義は VEF ではなく MySQL のコンポーネントフレームワークに属しており、サーバーはそれらをインストールしません。そのため mysql/components/services/*.h は、拡張機能 SDK にも、make install が構築するもの(リリース tarball や Docker イメージを含む)にも存在しません。サービスを利用する拡張機能は、VillageSQL サーバーのソースツリーに対してビルドします: ツリー内のテスト拡張機能は、vsql_add_test_extension()MYSQL_HEADERS フラグから両方を取得します。このフラグはそれらを MYSQL_INCLUDE_DIR および MYSQL_GENERATED_INCLUDE_DIR として渡します。ツリー外のビルドは独自のインクルードパスを設定します。 2つのビルド失敗は、原因となった行とは別の場所で報告されます。 サービスの MySQL ヘッダーを省略すると、VSQL_REQUIRE_SERVICE に何も解決しない名前が残るため、エラーは不足しているインクルードではなくマクロの位置に表示されます(clang 17):
一部のサービス定義は <cstddef> を含めずに size_t を使用するため、それらのヘッダーの1つをすべての villagesql ヘッダーより前に置くと、MySQL 自身のヘッダー内で失敗します:
このページの例のように、<cstddef> を最初に含めてください。

サービスの呼び出し

サービス参照は独自の valid() を公開し、-> はサービスに転送します。参照には . を、サービスには -> を使用します:
すべての -> 呼び出しの前に valid() を確認してください。-> は取得されたポインタを返しますが、サービスが取得されなかった場合そのポインタは NULL です。 取得に失敗したサービスはインストールを失敗させるため、実行中の関数の内部では、必要なサービスは有効です。それでも確認は重要です。手動で宣言され require() に渡されなかった ServiceRef には何も書き込まれないためです。コンパイルは通り、拡張機能はインストールされ、valid() は拡張機能の生存期間を通じて false のままになります。 サービスが何であるか — そのメソッド、パラメータ、戻り値 — は、ここではなく MySQL によって文書化されています。NAME という名前のサービスについては、サーバーツリーの include/mysql/components/services/NAME.h を読んでください。その BEGIN_SERVICE_DEFINITION(NAME) ブロックが、各メソッドを独自のドキュメントとともに宣言しています。bool の戻り値が false の場合は成功、true の場合は失敗を意味するという MySQL の慣習を含め、そのヘッダーが指定するとおりに呼び出してください。

取得の失敗

宣言されたすべてのサービスは、拡張機能のロード時、その関数が呼び出せるようになる前に取得されます。そのため、登録されていないサービスは後から表面化するのではなくロードを失敗させます。INSTALL EXTENSION は失敗し、そのサービス名を示します。 以下の vsql_mysql_services_missing_test は、レジストリに存在しないサービスを必要とするツリー内のテスト拡張機能です。これはインストールできるものではありません — これは失敗を記録した方法であり、このサーバーが提供しないサービスをあなた自身の拡張機能が必要とする場合に生成される出力です:
他に2つのインストール失敗が、この機能の外側から同じ機能に到達します:MysqlServices オブジェクトを .with() に渡さない場合と、vsql_allow_preview_extensions が OFF のサーバーにインストールする場合です。どちらも登録パターンで説明しています。

完全な例

サーバーの villagesql/test-extensions/ ツリーにある vsql_mysql_services_session_test の簡略化されたバージョンです。2つのサービスを組み合わせて、呼び出し元のセッションで実行中の SQL コマンドを読み取ります。一方は現在の THD を返し、もう一方はそこから名前付き属性を読み取ります。どちらもすべてのサーバーに登録されているサーバーコアのサービスであるため、事前に何かをインストールする必要はありません:
インストールして関数を呼び出します:

ステータス変数

status_var 機能(vsql::status_var)は、拡張機能が MySQL ステータス変数として long long および double カウンターを公開できるようにします。拡張機能はストレージを所有し、値を書き込みます。サーバーはステータス変数がクエリされるたびにポインタ経由で読み取ります。 vsql::preview_status_var::make_capability() で機能を構築し、make_int(name, value_ptr) または make_double(name, value_ptr) から得られる記述子のブレースリストを渡します。テンプレートはブレースリストからカウントを推測するため、明示的なサイズは必要ありません。

完全な例

make_int には long long * が必要です。make_double には double * が必要です。これらがサポートされる唯一の2つの型です。

SQL からのアクセス

INSTALL EXTENSION my_ext 後、変数は拡張機能名をプレフィックスとして表示されます:
複数のクエリスレッドが非アトミックな ++ で同時にインクリメントする場合、まれに値が失われる可能性がありますが、SHOW STATUS で公開される近似カウンターとしては許容されます。

システム変数

sys_var 機能(vsql::sys_var)は、拡張機能所有のストレージに紐付けられた MySQL システム変数を登録できるようにします。4つの型がサポートされます:BOOLbool *)、INTlong long *)、DOUBLEdouble *)、STRchar **)。INT および DOUBLE 記述子には min_val および max_val の境界も含まれます。すべての記述子にはデフォルト値とコメントが含まれます。 vsql::preview_sys_var::make_capability() で機能を構築し、対応するファクトリ関数 make_boolmake_intmake_doublemake_str を使用します。機能オブジェクトは get() および set() を公開し、拡張機能コードからのプログラム的アクセスを可能にします。両方とも成功時に false を返します。 値の変更に反応するには、記述子に .on_change<&fn>() をチェーンします。コールバックは var_name() および型付きアクセサ(as_int()as_real()as_str())を備えた sv::SysVarChange を受け取ります。 サーバーは、グローバルなシステム変数ロックを保持したままそのコールバックを呼び出します。その中でこの拡張機能の別の変数をストレージポインタ経由で読み書きすることは安全です。また、サーバーが同じロックの下でそれらの変数を読み取るため、他のセッションはすぐに新しい値を参照できます。
機能の get() または set() を呼び出すこと、SQL を実行すること、あるいはそのいずれかを行うスレッドを待機することは、そのロックでデッドロックします。コールバックは短くブロックしないように保ち、SQL を必要とする処理はスレッドワーカーに渡すか、sql/sys_vars.ccevent_scheduler_update() が行うように、ブロックする部分の前後で LOCK_global_system_variables を解放し、戻る前に再取得してください。
機能オブジェクトは静的ストレージ持続期間でなければなりません。MySQL はユーザーが変数を設定する際にストレージポインタに直接書き込みます。

完全な例

SQL からのアクセス

INSTALL EXTENSION my_ext 後、変数には拡張機能名をコンポーネントプレフィックスとしてアクセスできます:

拡張機能コードからの読み書き

INT および BOOL 変数については、グローバルストレージポインタを直接読み取ります — MySQL はそれらをアトミックに更新します。MySQL を介して変数を更新するには(ロック、範囲検証、永続化をサーバーが処理するため)、SYS_VARS.set(extension_name, var_name, scope, value) を呼び出します。set および get は成功時に false を返します。どちらも on_change コールバックから呼び出すことはできません。両方ともシステム変数ロックでデッドロックします。
scope 引数は永続化を制御します:

スレッドワーカー

thread worker 機能(vsql::preview::thread_worker)は、サーバーによって駆動されるバックグラウンドスレッドを拡張機能が実行できるようにします。スレッドは、サーバーが拡張機能ロード時に登録する制御システム変数を介して開始および停止されます。サーバーは、定期タイマー、ファイルディスクリプタの準備完了(レディ状態)、有効/無効イベントに応じて拡張機能の作業関数を呼び出します。 機能名 VEF_PREVIEW_THREAD_WORKER_NAME"vsql::preview::thread_worker" です。

機能の宣言

ヘッダーを含め、ファイルスコープで作業関数をインスタンス化した ThreadWorkerCapability を宣言し、.with() に渡します:
作業関数は非型テンプレート引数(ThreadWorkerCapability<&my_work>)として渡されるため、以下のシグネチャを持つ関数でなければなりません。最初のコンストラクタ引数はスレッド名サフィックスです。オプションの2番目の引数は制御 sys var 名をオーバーライドします。

作業関数シグネチャ

reason はサーバーが関数を呼び出した理由を示します。thread はサーバー所有のハンドルです(最初の VEF_WAKEUP_ENABLE 呼び出し時は NULL — 以下参照)。arg は記述子に登録された不透明ポインタで、変更されずに渡されます。

Wakeup ライフサイクル

サーバーは4つの理由のいずれかで作業関数を呼び出します: 理由が VEF_WAKEUP_ENABLE の場合、thread パラメータは NULL です。この時点でスレッドハンドルはまだ存在しないためです。他の3つの理由では thread は非 NULL です。

Wakeup 戻り値

作業関数は vef_next_wakeup_t を返し、次回のワーカー起動設定を更新します。各フィールドのゼロ値は「現在の設定を維持」を意味します — 両方を変更しない場合は値初期化構造体を返します(return {};)。 新しい poll ファイルディスクリプタを設定するには、その値を返します(0より大きい必要があります)。既存の poll ファイルディスクリプタをクリアするには、poll_fd-1 を返します。 理由が VEF_WAKEUP_DISABLE の場合、戻り値は無視されます。

スレッド名と制御変数

記述子の2つのフィールドが命名を制御します:
  • suffix — スレッド名サフィックス。サーバーは拡張機能名を前置し、my_ext/monitor のようなスレッド名を生成します。
  • var_name — オプション。NULL でない場合、サーバーはこの名前を制御システム変数として登録します。NULL の場合、サーバーはデフォルトパターン {suffix}_enabled を使用します。
制御変数はサーバー登録のシステム変数であるため、拡張機能名をコンポーネントプレフィックスとして取ります。サフィックス monitor を持つ拡張機能 my_ext の場合、変数は my_ext.monitor_enabled です。 ON に設定するとワーカーが開始されます。サーバーは VEF_WAKEUP_ENABLE で作業関数を呼び出してからスレッドを作成するため、その最初の呼び出しが終了するまで文は戻りません。ワーカーがすでに実行中に再度 ON に設定しても何も起こりません。OFF に設定した場合、スレッドが終了した後に戻ります。サーバーはその両方の前後でグローバルなシステム変数ロックを解放するため、作業関数はシステム変数の読み取りと SQL の実行ができます。

完全な例

単一の定期ワーカーを備えた最小限の拡張機能で、タイマー刻みごとにハートビートカウンターをインクリメントします。
この拡張機能をインストールし(vsql_allow_preview_extensions = ON が設定されている場合)、サーバーは拡張機能名の下に heartbeat_enabled システム変数を登録します。my_ext という名前の拡張機能の場合、ワーカーを有効化するには:

SQL クエリ

sql_query 機能(vsql::preview::sql_query)は、バックグラウンドスレッドから SQL 文を実行できるようにします。クエリは機能の vtable を介してサーバー内部で実行され、拡張機能は MySQL クライアントライブラリをリンクしません。 機能名 VEF_PREVIEW_SQL_QUERY_NAME"vsql::preview::sql_query" です。
SQL セッションは、スレッドワーカーのコールバックからそのコールバックの vef_thread_handle_t * を使用して開く必要があります。open() は VDF または任意の拡張機能作成スレッドから有効ではありません — これはワーカーセッションコンテキストを必要とします。

機能の宣言

ヘッダーを含め、ファイルスコープで SqlQueryCapability を宣言し、.with() に渡します。通常、ThreadWorkerCapability と共に登録されます。セッションはワーカーのコールバックから開かれるためです:
g_sql.open(handle)Session を返します。使用前に operator bool でチェックしてください。無効な Session は機能 vtable がバインドされていないか、サーバーがセッションを割り当てられなかったことを示します。Session はムーブオンリーで、破棄時に自動的に閉じられます。

クエリの実行

Sessionsession.sql(sv)SqlQuery を生成します。クエリは2つのモードで実行できます:
  • execute() — 文を実行し、完全な結果セットを Result にバッファリングします。next() を呼び出して行をイテレートします。
  • for_each(fn) — 文を実行し、行が生成されるたびに fn を1回呼び出します。バッファリングしません。返される Result には診断情報のみ(行なし)が含まれます。
両方とも Result を返します。非 NULL の Result は文が成功したことを意味しません — has_error() を呼び出して確認してください。 バッファリング(execute):
column_str()string_view を返します。これは次の next() 呼び出しまたは Result 破棄まで有効です。より長い寿命が必要な場合はコピーしてください。data() == nullptrstring_view は SQL NULL を示します。 ストリーミング(for_each):
コールバックに渡される Row はコールバックの実行中のみ有効です — 行間で参照を保持しないでください。for_each が返す Result はバッファリングされた行を持たず、next() はデータを返しません。has_error()error()warning_count()warning(i) のみに使用してください。

診断

execute() および for_each() は返される Result を介して診断情報を返します。診断は1つの Diag です:
Result は以下を公開します:
error() は文が成功した場合、デフォルト構築された Diagerrno_ == 0)を返します。warning(i)i >= warning_count() の場合、デフォルト構築された Diag を返します。 sqlstate および message のビューは Result が所有するストレージを指しており、Result が破棄されると無効になります — Result の生存期間を超えて保持する必要がある場合はコピーしてください。

完全な例

タイマー刻みごとに1つのバッファリングクエリと1つのストリーミングクエリを実行し、両方の診断をログ出力するワーカー:

カラムストレージ

カラムストレージは、拡張機能が InnoDB に直接カスタム型のバイナリディスクレイアウトを登録できるようにします。これにより、VARBINARYペイロード経由で型のバイトをルーティングするのではなく、カスタム型のディスク形状を直接表現できます。例えば、専用ページに格納される必要のある圧縮浮動小数点配列など、VARBINARYが表現できないディスク形状が必要な場合に使用します。これは新しいストレージレイアウトを可能にする機能追加であり、既存のレイアウトのチューニングスイッチではありません。
カラムストレージはプレビューABIです — 開発中であり、リリース間で変更される可能性があります。現在、行レベルの永続化のみをカバーしており、カスタムストアードカラムのインデックスはまだ利用できません。

機能の宣言

2つのプレビュー機能が連携して動作します:
  • vsql::preview::storage — InnoDB ストレージインフラストラクチャ(ミニトランザクション、セグメント、ページ)へのアクセスを提供します。ファイルスコープで StorageCapability を宣言します。
  • vsql::preview::column_store — 型ごとのストレージ実装を、拡張機能のカスタム型の1つにバインドします。make_column_store<Ctx>(TYPE).…build() を使用してファイルスコープで ColumnStoreCapability を宣言します。
両方を make_extension().with() に渡す必要があります:
make_column_store<MyCtx>(MY_TYPE) は実装を同じ拡張機能で登録された1つのカスタム型に紐付けます。build() 時に7つのスロットが必要です。各スロットは InnoDB が通常動作中に到達するカラムライフサイクルの異なるポイントに対応します。

7つのストレージ関数

すべての関数は storage::Column::StorageCtx<MyCtx>* を受け取ります。user() アクセサは拡張機能のカラムごとの状態を返し、arena() は補助オブジェクトのサーバー管理割り当てを提供します。すべての関数は成功時に false を返し、エラー時に true を返し、失敗が SQL クライアントに伝わるように error_msg(容量 error_msg_len)にメッセージを書き込みます。
mark_deletepurge は区別されます。InnoDB MVCC では、削除された行は purge が実行されるまで古いスナップショットで読み取り可能でなければなりません。

カラムごとのコンテキストとアリーナ

C++ SDK は create または load を呼び出す前に MyCtx をデフォルト構築します — 関数が実行される時点で ctx->user() には既に値が設定されています。MyCtx はデフォルト構築可能でなければなりません。C++ SDK は引数なしで T() を呼び出します。 ctx->user() を直接使用して状態を初期化します。ctx->arena().construct<MyCtx>() を呼び出さないでください — これは不要なインスタンスを割り当てますが、ctx->user() はその領域を指していません。
load も同様のパターンです — ctx->user() には既に値が設定されており、storage_refcreatectx->set_ref() が格納したパックされた値を保持します:
ctx->arena()MyCtx に直接埋め込むことができないほど大きいか動的な補助オブジェクトを割り当てる場合にのみ使用します。C++ SDK は、drop が成功したかどうかに関係なく、drop から戻った後にアリーナを自動的に破棄し(~MyCtx() を呼び出し)ます。

InnoDB アクセスユーティリティ

InnoDB プリミティブには <villagesql/preview/storage_api.h> を含めます。すべてのページ読み書きはミニトランザクション内で行われます:
ミニトランザクションのコミットはページラッチを解放し、変更を永続化する redo ログレコードを書き込みます。 セグメントcreate 時に予約されます — セグメントのセットアップパターンについては、上記の「カラムごとのコンテキスト」の create および load 例を参照してください。DML操作中、ルートページからセグメント参照を取得して新しいページを割り当てます:
ページ は共有ラッチで読み取り、排他ラッチで書き込みます。InnoDB が変更をログに記録するため、mtr_ref を書き込み呼び出しに渡します:
ページレイアウト定数: ヘッダーまたはトレーラー領域内での読み取りまたは書き込みはページを破損させます — InnoDB はこれらのバイト範囲を自身の管理およびチェックサムに使用しています。

ステートメントイベント

statement event 機能(vsql::preview::statement_event)は、各クエリの実行完了後に拡張機能提供のハンドラを実行します。サーバーはハンドラをクエリ自身のスレッドで同期的に呼び出し、実行メタデータ — クエリテキスト、タイミング、行数、接続元の識別情報、オプティマイザ品質インジケータ — を渡します。スロークエリのログ記録、監査、メトリクス収集に使用します。 機能名 VEF_PREVIEW_STATEMENT_EVENT_NAME"vsql::preview::statement_event" です。

機能の宣言

発火フェーズとハンドラ関数でインスタンス化した StatementEventCapability をファイルスコープで宣言し、.with() に渡します:
最初のテンプレート引数は発火フェーズで、vef_statement_event_phase_t 値です。VEF_STATEMENT_EVENT_POSTEXECUTE はクエリの実行完了後に、成功または失敗にかかわらず発火し、このバージョンで実装されている唯一のフェーズです。その他の vef_statement_event_phase_t 値は予約されており、そのいずれかに対してハンドラを宣言すると、サーバーは INSTALL EXTENSION を拒否します。

ハンドラ引数

StatementEventArgs は完了したクエリの読み取り専用ビューです。POSTEXECUTE フェーズではすべてのフィールドに値が設定されています。主なアクセサ: query() は存在する場合サーバーの書き換え形式を返すため、認証情報を含む文はシークレットが平文ではなく難読化された状態で到着し、一般ログ、スローログ、バイナリログが既にそれらを難読化(伏字化)する方法と一致します:SET PASSWORDCREATE/ALTER USER ... IDENTIFIED BYCHANGE REPLICATION SOURCE ... SOURCE_PASSWORDCREATE SERVER ... OPTIONS(PASSWORD ...)。書き換えルールのない文はそのまま配信されます。 query()sqlstate()error_message() などの文字列アクセサは、ハンドラ呼び出しの実行中のみ有効なストレージを指しています — ハンドラが返った後にそれらが必要な場合はバイトをコピーしてください。 StatementEventResult::error_msg(fmt, ...) は printf 形式のメッセージを書き込みます。POSTEXECUTE フェーズでは、メッセージは参考情報(アドバイザリ)であり、サーバーはログに記録しますが、クライアントには伝播しません。

完全な例

vsql_slow_query_log テスト拡張機能の簡略化された形式です。実行時間がしきい値を超える各クエリをログに記録し、statement event 機能をシステム変数と組み合わせて実行時設定を行います:

SQL からの有効化

プレビュー層を有効化した上で(プレビュー層の有効化を参照)、拡張機能をインストールし、そのシステム変数を通じて設定します:
しきい値より遅い各クエリは、設定されたログファイルに追記されます:

認証方式

auth 機能(vsql::preview::auth)は、拡張機能がサーバーの認証方式を提供できるようにします。アカウントは CREATE USER ... IDENTIFIED WITH <method-name> でこれを選択します。接続時に、その名前がロード済みの MySQL 認証プラグインでない場合、サーバーは VEF 認証レジストリを参照し、ハンドシェイクを通じて拡張機能のハンドラを呼び出します。MySQL 認証プラグインを書かずに、サーバーが知らない認証情報ソース — ベアラートークン、外部 ID プロバイダー、カスタムチャレンジなど — に対してアカウントを認証する場合に使用します。 機能名 VEF_PREVIEW_AUTH_NAME"vsql::preview::auth" です。 ハンドラは AuthContext を受け取る型付き関数です。サーバー所有のそのコンテキストを介してハンドシェイクパケットを読み書きすることでクライアントと通信し、MySQL の内部認証構造体を参照することはありません。
認証結果はフェイルクローズです。サーバーは AuthResult::kOk 以外のすべてを接続拒否として扱います — 「たぶん」やフェイルオープンの結果は意図的に存在しません。AuthResult::kReject を返すハンドラ、AuthResult::kError を返すハンドラ、または実効アカウントを設定しないハンドラは、ログインを拒否します。

機能の宣言

ヘッダーを含め、型付きハンドラを記述し、make_auth<> ビルダーのチェーン呼び出しで記述子を構築し、その記述子を .with() に渡す AuthCapability トークンに渡します。プレビュー機能のヘッダーは <villagesql/vsql.h> のアンブレラには含まれないため、<villagesql/preview/auth.h> を明示的に含めてください:
ビルダーは6つの要素で構成されます: AuthCapability g_auth{descriptor}.with() が消費する自己登録トークンです。登録より長く存続するように static として宣言してください。 client_plugin はオプションです。make_auth は通知するプラグインを既定で "mysql_clear_password" — すべての MySQL クライアントが同梱する最も基本的なプラグイン — に設定します。そのため .client_plugin() を一度も呼び出さない方式でもインストールでき、単純なクライアントでも接続できます。別のプラグインを要求するには .client_plugin(name) を呼び出します。mysql_clear_password はパスワードスロットでベアラートークンをそのまま受け取ります。 方式が要求するものとは別のプラグインを提示するクライアントは、要求されたプラグインに切り替えられ、認証情報をそのまま再送します。これには1往復のコストがかかり、その切り替えに応じるクライアントが必要です。.accepts_client_plugin(&callback) を使うと、方式は提示されたプラグインをそのまま維持できます。サーバーは提示された各名前をコールバックに渡します。要求されたプラグインも渡されますが、これはコールバックの戻り値にかかわらず受け入れられます。コールバックを設定しない方式は他の提示を一切受け入れないため、他のすべての提示は要求されたプラグインに切り替わります。受け入れは最終的です — サーバーはその後で要求されたプラグインに切り替え直しません — したがって、ハンドラが実際にフレーミングを解析できるプラグインだけを受け入れてください。サーバーはハンドシェイクのネゴシエーション中、ハンドラの最初の読み取りより前にコールバックを呼び出すため、コールバックは純粋な述語でなければなりません:パケット I/O、ブロッキング、副作用のいずれも行わないでください。

ハンドラの契約

ハンドラは AuthHandler 型に一致します — AuthContext & を取り、AuthResult を返します:
ハンドラは、ハンドシェイク中に接続スレッド上で同期的に呼び出されます。AuthContext は、サーバー所有の試行ごとのコンテキストをラップします。呼び出しの実行中のみ保持し、保存しないでください。関数テーブルを通じてコンテキストポインタを引き回す代わりに、そのメソッドを呼び出してください。トークンベースのハンドラが使用するメソッド: ハンドラは3つの結果のいずれかを返します: AuthResult::kRejectAuthResult::kError はどちらも接続を拒否します。成功するのは AuthResult::kOk のみです。 ハンドラが接続アカウントを別の実効アカウントにマッピングする場合 — 以下の例が接続アカウントを vsql_auth_test_user にマッピングするように — それはプロキシであり、MySQL のプラグイン認証の経路とまったく同じように GRANT PROXY が必要です。

アクティブロールのステージング

c.set_active_roles(roles, n_roles) は、このログインにおいてアカウントのデフォルトロールの有効化に代えて、セッションでアクティブにすべきロールをステージングします。rolesn_roles 個の NUL 終端名の配列です。文字列はコピーされるため、呼び出し側が保持し続ける必要はありません。サーバーはアカウント解決のに、SET ROLE と同じ付与チェック付きの有効化を使用してそれらを適用します。実際に認証済みアカウントに付与されているロールのみが有効になり、付与されていない名前は黙ってスキップされます — したがってトークンが DBA の用意した範囲を超えて権限を付与したり昇格させたりすることはできません。n_roles == 0 を渡すとロールは有効になりません(SET ROLE NONE と同等)。

完全な例

サーバーソースツリーの villagesql/test-extensions/vsql-auth-test/ にある vsql_auth_test 拡張機能を凝縮した、最小限の認証機能です。この拡張機能はどのリリースにも含まれていません。固定のトークンを1つ受け入れ、接続を vsql_auth_test_user にマッピングし、トークンがパスワードスロットにそのまま届くように mysql_clear_password を要求します。(ツリー内の拡張機能は、テストスイートを動かすために追加のトークン経路、.accepts_client_plugin() コールバック、および以下で説明する2つのオプトインを備えています。)

アカウントのバインドと接続

プレビュー層を有効化した上で(プレビュー層の有効化を参照)、拡張機能をインストールし、アカウントをこの方式にバインドします。ハンドラは2つ目のアカウントにマッピングするため、そのアカウントも作成し、接続アカウントがその ID を引き継げるようにする PROXY 権限を付与します:
vsql_auth_test は登録済みの VEF 認証方式であるため、CREATE USER ... IDENTIFIED WITH vsql_auth_test は受け入れられます — インストール済みのプラグイン名が受け入れられるのと同じ仕組みです。 受け入れられるのは IDENTIFIED WITH <method> の形式のみで、オプションで AS '...' を付けられます。BY '...' を追加すると、パスワードを保存用の認証情報に変換することを方式に要求することになります — MySQL のプラグインが generate_authentication_string() を通じて行う処理です — が、現在そのフックを宣言する VEF 認証方式はないため、サーバーはこれを拒否します:
バインドされた方式名は、テーブルのデフォルトではなくアカウントの plugin カラムに書き込まれます。これがアカウントの次回ログイン時に読み取られる値です:
この方式は mysql_clear_password を要求するため、クライアントはトークンを平文で送信するために --enable-cleartext-plugin を渡す必要があります。トークンが正しい場合、セッションはマッピングされたアカウントとして実行され、接続アカウントを @@external_user を通じて公開します:
拡張機能をアンインストールすると方式が削除されます。それにバインドされたアカウントは認証できなくなります:

アカウントの自動作成

方式は、まだ存在しないアカウントのログインを処理し、ログイン成功の一部としてサーバーにそのアカウントを作成させることもできます。これがない場合、未知のアカウントはどの方式が実行されるよりも前に拒否されます。 .auto_create(&callback) でオプトインします。コールバックは引数を取らず bool を返します。サーバーは登録時に一度読み取るのではなく、未知のアカウントによるログインのたびにこれを呼び出します。そのため方式は、拡張機能のロード時に選択を固定するのではなく、自身の実行時設定に従うことができます:
.auto_create() を付けない場合、またはコールバックが false を返す場合、標準の動作が維持されます:未知のアカウントは拒否されます。同時にオプトインできるインストール済みの方式は1つだけです — 2つが true を返した場合、サーバーは推測を行わず、エラーログに警告を記録し、どの方式もオプトインしていない場合と同じように未知のアカウントを拒否します。 ハンドラ内では、c.account_unknown() が2つのケースを区別します。まず認証情報を検証し、次に何を作成するかを指定して、そのアカウントとして認証します:
request_provision(account, roles, n_roles) は意図を記録するだけで、何も返しません。サーバーは、ハンドラが AuthResult::kOk を返した後に DDL を自身で実行します。実行するのは、未知のアカウントとしてルーティングされたログインの場合のみです — そのため、ハンドラがその後で拒否するログインは何も作成せず、すでに存在するアカウントを指定した要求は無視されます。サーバーが実行するのは CREATE USER IF NOT EXISTS <account>@'%' IDENTIFIED WITH <method> で、続いて指定されたロールごとに1つの GRANT を実行します。アカウントは常にホスト % に対して作成され、認証を行った方式にバインドされます。また account は接続ユーザー名である必要はありません。作成ができない場合 — 例えば super_read_only のサーバーでは — アカウントなしで処理を続行するのではなく、ログインが失敗します。 ロールの扱いはアクティブロールのステージングと同じです:ロールは DBA が管理します。各名前は付与可能なロールとしてすでに存在している必要があり、付与できないものはログインを失敗させるのではなくログに記録されてスキップされます。そのためトークンはロールを指定できますが、ロールを作成したり昇格させたりすることはできません。アカウント名はクライアントから渡されるため、サーバーはそれを識別子として引用符で囲みます — 細工された名前は奇妙な名前のアカウント1つになるだけで、2つ目の文になることはありません。 vsql_auth_test 拡張機能は、接続ユーザーにロール vsql_role_granted を付与してプロビジョニングし、オプトインを vsql_auth_test.auto_create で制御します。この変数の初期値は OFF です。まずこれをオンにしてロールを作成し、その後で存在しないアカウントとして接続します:
アカウントが作成され、方式にバインドされ、付与されたロールを保持しています:
誤ったトークンは引き続きフェイルクローズで失敗し、何もプロビジョニングしません:
オプトインすると、有効な認証情報を持つ者にとって、未知のアカウントと既存のアカウントの違いが観測可能になります。標準の未知アカウント拒否は、この違いを意図的に隠しています。これがこの機能のトレードオフです。認証情報が広く共有されている方式でオプトインを有効にする前に、この点を検討してください。

ロールの自動付与

既定では、トークンが指定したロールは、アカウントがすでにそれを保持している場合にのみ有効になり、保持していないものはログに記録されてスキップされます。.auto_grant(&callback) はこれを変更します:サーバーはステージングされたロールをアカウントに付与します。そのためトークンは、アカウントの既存のロールのうちどれを有効にするかだけでなく、セッションがどのロールを得るかを決定します。 コールバックの形は .auto_create() と同じです — 引数を取らず、bool を返し、サーバーはログインのたびにこれを呼び出すため、実行時設定に従うことができます:
2つのオプトインは独立しています。.auto_create() は存在しないアカウントのログインを制御します。.auto_grant() は、そのアカウントが今作成されたかどうかにかかわらず、ログインが解決したアカウントへの付与を制御します。.auto_grant() を付けない場合、または false を返す場合、有効化のみを行う既定の動作が維持されます。 付与は永続します — これはセッション限りの有効化ではなく通常の GRANT です — そして加算的です:サーバーは、トークンが指定しなくなったロールを取り消すことはありません。 vsql_auth_test はこれを vsql_auth_test.auto_grant として公開します。この変数も初期値は OFF です。この拡張機能の -token-roles トークンは vsql_role_grantedvsql_role_denied をステージングしますが、以下のアカウントはどちらも保持していません。設定がオフの場合、ログインはアカウントのロールをそのままにします:
上記と同じ方法でそのトークンを使って auth_user として接続し、何が有効かを確認します:
設定をオンにして、同じログインを繰り返します:
両方のロールが有効になり、SHOW GRANTS はサーバーが追加した付与を表示します:
.auto_grant() がオンの場合、有効なトークンがあれば、それが指定するどのロールも取得できます。ロールはすでに存在している必要があるため、トークンが権限を作り出すことはできませんが、アカウントがどの既存ロールに到達できるかを決めるのは DBA ではなく方式になります。