vsql_allow_preview_extensions = ON が設定されている場合にのみインストールできます(プレビュー層の有効化を参照)—プレビュー機能を使用しない拡張機能は、この設定に関係なく通常通りインストールされます。
プレビュー層の有効化
プレビュー機能を使用する拡張機能をインストールする前に、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。このメッセージは、どの機能が原因であったかは示しません。
Keyring アクセス
keyring 機能(vsql::preview::keyring)は、MySQL keyring コンポーネントに格納されたシークレットを拡張機能が読み書きできるようにします。APIキー、暗号化キー、またはSQLテーブルに保存したくないその他のシークレットに使用します。
機能名 VEF_PREVIEW_KEYRING_NAME は "vsql::preview::keyring" です。
keyring コンポーネント(たとえば component_keyring_file)は、Keyring Component Installation に記載されているとおり、コンポーネントマニフェストとその設定ファイルを通じてサーバー起動時にロードされている必要があります。INSTALL COMPONENT ではそれを利用可能にできません。ロードされていない場合、操作は KeyringCapability::Status::UNAVAILABLE を返します。
ステータス値
KeyringCapability::Status は read()(ReadResult 内)および write() から返されるスコープ付き列挙型です:
機能の宣言
ヘッダーを含め、ファイルスコープで機能オブジェクトを宣言し、.with() に渡します:
g_keyring オブジェクトはロード時にサーバーによって初期化されます。keyring コンポーネントがインストールされていない場合、read() および write() は実行時に Status::UNAVAILABLE を返します — 別の利用可能性チェックで処理をガードする代わりに、各呼び出しでステータスを確認してください。
読み書き
data_id はキー識別子です。auth_id は所有ユーザーです — 内部キー(特定のユーザーに関連付けられていない)を読み書きするには空文字列(または read では省略、デフォルトは {})を渡します。
read は値で ReadResult を返します。構造化バインディングでバインドします:
Status::OK 以外のステータスの場合、value は空です。
write は直接 Status を返し、data を data_id / auth_id で格納します。component_keyring_file では、既存のキーへの書き込みは Status::ERROR を返します。
完全な例
これはサーバーの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 を宣言し、name を services に登録します。var は自動的に static として宣言されます。MysqlServices オブジェクトも static である必要があり、手動で宣言する参照も同様です。サーバーはロード時にそれらを介して書き込むため、それらは拡張機能より長く存続する必要があります。
特定の実装の指定
VSQL_REQUIRE_SERVICE は name を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 は、レジストリに存在しないサービスを必要とするツリー内のテスト拡張機能です。これはインストールできるものではありません — これは失敗を記録した方法であり、このサーバーが提供しないサービスをあなた自身の拡張機能が必要とする場合に生成される出力です:
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つの型がサポートされます:BOOL(bool *)、INT(long long *)、STR(char **)、そして dev ABI でのみ ENUM(unsigned long *、固定された順序付きの名前リストに対するゼロ始まりのインデックスを格納)。INT 記述子には min_val および max_val の境界も含まれます。すべての記述子にはデフォルト値とコメントが含まれます。
vsql::preview_sys_var::make_capability() で機能を構築し、対応するファクトリ関数 make_bool、make_int、make_str、および make_enum(dev ABI のみ)を使用します。機能オブジェクトは get() および set() を公開し、拡張機能コードからのプログラム的アクセスを可能にします。両方とも成功時に false を返します。
値の変更に反応するには、記述子に .on_change<&fn>() をチェーンします。コールバックは var_name() および型付きアクセサ(as_int()、as_str()、そして dev ABI でのみ as_enum())を備えた sv::SysVarChange を受け取ります。
サーバーは、グローバルなシステム変数ロックを保持したままそのコールバックを呼び出します。その中でこの拡張機能の別の変数をストレージポインタ経由で読み書きすることは安全です。また、サーバーが同じロックの下でそれらの変数を読み取るため、他のセッションはすぐに新しい値を参照できます。
機能オブジェクトは静的ストレージ持続期間でなければなりません。MySQL はユーザーが変数を設定する際にストレージポインタに直接書き込みます。
enum 変数は名前(
SET GLOBAL my_ext.mode = 'sync')または名前のゼロ始まりのインデックスで設定します。いずれの場合も、サーバーは受け入れる前に値を names と照合して検証し、認識できない名前や範囲外のインデックスは格納せずに拒否します:
make_str との違いです。値の空間は閉じており、SET 時に強制されます。便利なオーバーロードは static const char *const NAMES[] = {...} 配列から name_count を推論するため、個数の引数は省略できます。
完全な例
この例はmake_enum を使用するため、dev ABI(-DVSQL_USE_DEV_ABI=ON)でビルドしてください。
SQL からのアクセス
INSTALL EXTENSION my_ext 後、変数には拡張機能名をコンポーネントプレフィックスとしてアクセスできます:
拡張機能コードからの読み書き
INT および BOOL 変数については、グローバルストレージポインタを直接読み取ります — MySQL はそれらをアトミックに更新します。MySQL を介して変数を更新するには(ロック、範囲検証、永続化をサーバーが処理するため)、SYS_VARS.set(extension_name, var_name, scope, value) を呼び出します。set および get は成功時に false を返します。どちらも on_change コールバックから呼び出すことはできません。両方ともシステム変数ロックでデッドロックします。
scope 引数は永続化を制御します:
セッション変数
session_var 機能(vsql::session_var)は、セッションごと(THD ローカル)のシステム変数を拡張機能が登録できるようにします — プラグインの MYSQL_THDVAR_* に機能的に相当するものです。これは vsql::sys_var のセッションスコープ版です。sys_var はすべての接続で共有される1つの値を持ちますが、session_var は各接続に SET SESSION で設定する独自の値を与えます。新しいセッションの開始点は、記述子のデフォルト、または SET GLOBAL で最後に格納された値です。変数のスコープは登録時に固定されます — セッションごとの変数にはこの機能を、グローバルな変数には vsql::sys_var を使用してください。
サポートされる型は2つだけです:INT(long long)と STR(std::string)。BOOL や DOUBLE のセッション変数はなく、on_change コールバックの仕組みもありません — vsql::sys_var とは異なり、拡張機能はセッション変数の値を読み返すことによってのみ観測し、変更時に通知されることはありません。
機能の宣言
vsql::preview_session_var::make_capability() で機能を構築し、make_int(name, comment) または make_str(name, comment) から得られる記述子のブレースリストを渡します。各記述子はメソッドチェーン形式の .default_()、.min()、.max() セッターで設定します(min/max は INT にのみ適用されます):
make_int は制約なしで始まります(デフォルト 0、long long の全範囲)。make_str は空文字列のデフォルトで始まります。機能オブジェクトは、このページの他のすべてのプレビュー機能と同様に、静的ストレージ持続期間が必要です。
セッション値の読み取り
読み取りパスは2つあり、どちらも呼び出し元の接続のスレッドに対して解決されます — そのため、どちらも接続スレッド上(接続上で実行される VDF またはコールバック)で呼び出さなければならず、バックグラウンドのスレッドワーカーから呼び出してはいけません:SESSION_VARS.get_session_int(name, out)/get_session_str(name, out)— 名前をキーとし、呼び出しのたびに変数を再解決します。たまに読み取る場合にはこれを使用してください。またSTR変数にとってはこれが唯一のパスです。STRスロットは別途割り当てられたバッファへのポインタを保持しており、同時に実行されるSET SESSIONやUNINSTALLがそれを解放しうるため、ロックフリーの文字列用高速パスはありません。SESSION_VARS.int_var(name)は、最初のread()で自身を遅延解決して結果をキャッシュするIntVarを返すため、機能がロードされる前にファイル/静的スコープで宣言できます。それ以降の各read()は、ロックフリーのスレッドごとのオフセットロードです — 名前のルックアップもグローバルロックもなく — プラグインのTHDVAR(thd, var)に相当します。この高速パスはINTのみです。
SQL からのアクセス
INSTALL EXTENSION my_ext 後、各変数は拡張機能名をコンポーネントプレフィックスとして取り、@@session. と @@global. の両方で読み取りおよび設定できます:
SET SESSION の影響を受けず、自身の値を設定するまではコンパイル時のデフォルトを参照します。SET GLOBAL は、新しいセッションやリセットされたセッションが引き継ぐデフォルトを変更するのであって、すでに設定済みのどのセッションの値も変更しません。
範囲外の INT の代入は境界にクランプされて警告が出され、型の誤った代入は拒否されます。通常の MySQL システム変数とまったく同じです:
再起動時の動作
SET PERSIST と SET PERSIST_ONLY は、他の MySQL システム変数と同様に、セッション変数のグローバル値に対して機能します。値はサーバーの再起動後も維持され、各新しいセッションはその値から始まります。SET SESSION の値は接続ごとの状態であり、再起動後には維持されません。SET PERSIST を使用しない場合、拡張機能がインストールされたままの再起動後には、各変数はコンパイル時のデフォルトで再び現れます。
スレッドワーカー
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" です。
機能の宣言
ヘッダーを含め、ファイルスコープでSqlQueryCapability を宣言し、.with() に渡します。通常、ThreadWorkerCapability と共に登録されます。セッションはワーカーのコールバックから開かれるためです:
g_sql.open(handle) は Session を返します。使用前に operator bool でチェックしてください。無効な Session は機能 vtable がバインドされていないか、サーバーがセッションを割り当てられなかったことを示します。Session はムーブオンリーで、破棄時に自動的に閉じられます。
クエリの実行
Session は session.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() == nullptr の string_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() は文が成功した場合、デフォルト構築された Diag(errno_ == 0)を返します。warning(i) は i >= warning_count() の場合、デフォルト構築された Diag を返します。
sqlstate および message のビューは Result が所有するストレージを指しており、Result が破棄されると無効になります — Result の生存期間を超えて保持する必要がある場合はコピーしてください。
完全な例
タイマー刻みごとに1つのバッファリングクエリと1つのストリーミングクエリを実行し、両方の診断をログ出力するワーカー:カラムストレージ
カラムストレージは、拡張機能が InnoDB に直接カスタム型のバイナリディスクレイアウトを登録できるようにします。これにより、VARBINARYペイロード経由で型のバイトをルーティングするのではなく、カスタム型のディスク形状を直接表現できます。例えば、専用ページに格納される必要のある圧縮浮動小数点配列など、VARBINARYが表現できないディスク形状が必要な場合に使用します。これは新しいストレージレイアウトを可能にする機能追加であり、既存のレイアウトのチューニングスイッチではありません。機能の宣言
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 を返し、error_msg(容量 error_msg_len)にメッセージを書き込みます。サーバーはそのメッセージをエラーログに書き込みます。create または insert が失敗すると、ステートメントは ERROR 3219 (HY000): InnoDB: Custom type operation failed. See server error log for details. で失敗します。
mark_delete と purge は区別されます。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_ref は create で ctx->set_ref() が格納したパックされた値を保持します:
ctx->arena() は MyCtx に直接埋め込むことができないほど大きいか動的な補助オブジェクトを割り当てる場合にのみ使用します。C++ SDK は、drop が成功したかどうかに関係なく、drop から戻った後にアリーナを自動的に破棄し(~MyCtx() を呼び出し)ます。
InnoDB アクセスユーティリティ
InnoDB プリミティブには<villagesql/preview/storage_api.h> を含めます。すべてのページ読み書きはミニトランザクション内で行われます:
create 時に予約されます — セグメントのセットアップパターンについては、上記の「カラムごとのコンテキスト」の create および load 例を参照してください。DML操作中、ルートページからセグメント参照を取得して新しいページを割り当てます:
mtr_ref を書き込み呼び出しに渡します:
ページアクセサは、ヘッダーまたはトレーラー領域内のオフセットを拒否します。リリースビルドは読み取りまたは書き込みをスキップし、
NDEBUG なしのビルドは assert で失敗します。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 PASSWORD、CREATE/ALTER USER ... IDENTIFIED BY、CHANGE REPLICATION SOURCE ... SOURCE_PASSWORD、CREATE SERVER ... OPTIONS(PASSWORD ...)。書き換えルールのない文はそのまま配信されます。
query()、sqlstate()、error_message() などの文字列アクセサは、ハンドラ呼び出しの実行中のみ有効なストレージを指しています — ハンドラが返った後にそれらが必要な場合はバイトをコピーしてください。
StatementEventResult::error_msg(fmt, ...) は printf 形式のメッセージを書き込みます。POSTEXECUTE フェーズでは、メッセージは参考情報(アドバイザリ)であり、サーバーはログに記録しますが、クライアントには伝播しません。
完全な例
vsql_slow_query_log テスト拡張機能の簡略化された形式です。実行時間がしきい値を超える各クエリをログに記録し、statement event 機能をシステム変数と組み合わせて実行時設定を行います:
SQL からの有効化
プレビュー層を有効化した上で(プレビュー層の有効化を参照)、拡張機能を-DVSQL_USE_DEV_ABI=ON でビルドし、その .veb ファイルを veb_dir にコピーしてインストールし、そのシステム変数を通じて設定します:
認証方式
auth 機能(vsql::preview::auth)は、拡張機能がサーバーの認証方式を提供できるようにします。アカウントは CREATE USER ... IDENTIFIED WITH <method-name> でこれを選択します。接続時に、その名前がロード済みの MySQL 認証プラグインでない場合、サーバーは VEF 認証レジストリを参照し、ハンドシェイクを通じて拡張機能のハンドラを呼び出します。MySQL 認証プラグインを書かずに、サーバーが知らない認証情報ソース — ベアラートークン、外部 ID プロバイダー、カスタムチャレンジなど — に対してアカウントを認証する場合に使用します。
機能名 VEF_PREVIEW_AUTH_NAME は "vsql::preview::auth" です。
ハンドラは AuthContext を受け取る型付き関数です。サーバー所有のそのコンテキストを介してハンドシェイクパケットを読み書きすることでクライアントと通信し、MySQL の内部認証構造体を参照することはありません。
機能の宣言
ヘッダーを含め、型付きハンドラを記述し、make_auth<> ビルダーのチェーン呼び出しで記述子を構築し、その記述子を .with() に渡す AuthCapability トークンに渡します。プレビュー機能のヘッダーは <villagesql/vsql.h> のアンブレラには含まれないため、<villagesql/preview/auth.h> を明示的に含めてください:
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::kReject と AuthResult::kError はどちらも接続を拒否します。成功するのは AuthResult::kOk のみです。
ハンドラが接続アカウントを別の実効アカウントにマッピングする場合 — 以下の例が接続アカウントを vsql_auth_test_user にマッピングするように — それはプロキシであり、MySQL のプラグイン認証の経路とまったく同じように GRANT PROXY が必要です。
アクティブロールのステージング
c.set_active_roles(roles, n_roles) は、このログインにおいてアカウントのデフォルトロールの有効化に代えて、セッションでアクティブにすべきロールをステージングします。roles は n_roles 個の NUL 終端名の配列です。文字列はコピーされるため、呼び出し側が保持し続ける必要はありません。サーバーはアカウント解決の後に、SET ROLE と同じ付与チェック付きの有効化を使用してそれらを適用します。実際に認証済みアカウントに付与されているロールのみが有効になり、付与されていない名前は黙ってスキップされます — したがって、方式が .roles_mode() を設定しない限り、トークンが 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つのオプトインを備えています。)
アカウントのバインドと接続
プレビュー層を有効化した上で(プレビュー層の有効化を参照)、拡張機能を-DVSQL_USE_DEV_ABI=ON でビルドし、その .veb ファイルを veb_dir にコピーしてインストールし、アカウントをこの方式にバインドします。ハンドラは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 です。まずこれをオンにしてロールを作成し、その後で存在しないアカウントとして接続します:
ロールの自動付与
既定では、トークンが指定したロールは、アカウントがすでにそれを保持している場合にのみ有効になります。保持していないものはスキップされ、サーバーはその理由をログに記録します(VEF auth: role '<role>'@'<host>' requested for account '<user>'@'<host>' is not granted; skipping)。.roles_mode(&callback) はこれを変更します:コールバックは vef_auth_roles_mode_t — VEF_AUTH_ROLES_ACTIVATE(既定)、VEF_AUTH_ROLES_GRANT、または VEF_AUTH_ROLES_SYNC — を返し、サーバーはログインのたびに、ステージングされたロールをアカウントに付与済みのロールとそれに応じて調整します。
コールバックの形は .auto_create() と同じです — 引数を取りません — が、bool の代わりにモードの enum を返し、サーバーはログインのたびにこれを呼び出し、その裏付けとなるものをライブで読み取ります:
.auto_create() は、まだ存在しないアカウントのログインにアカウントのプロビジョニングを許可するかどうかを決定します。.roles_mode() はその後に、ログインが解決したアカウント — 今プロビジョニングされたアカウント、またはすでに存在していたアカウント — に対して実行されるため、どちらの場合も同じ調整が適用されます。VEF_AUTH_ROLES_ACTIVATE、またはコールバックをまったく付けない場合、有効化のみを行う既定の動作が維持されます。
VEF_AUTH_ROLES_GRANT は加算的です:アカウントが持たないステージング済みロールを付与し、取り消しのステップをまったく実行しません。VEF_AUTH_ROLES_SYNC はそのステップを実行します:トークンが主張しなかった付与済みロールをすべて取り消すため、トークンから外されたロールは、次回のログイン時にアカウントからも外されます — ただし、オペレーターの ALTER USER によってアカウントの DEFAULT ROLE として設定されたロールは例外で、SYNC はこれを決して取り消しません。どちらのモードでも、DB ロールとして存在しないステージング済みロールは GRANT に失敗し、ログインを失敗させるのではなく、ログに記録されてスキップされます。
vsql_auth_test はこれを vsql_auth_test.auto_grant として公開します。これは OFF/ON/SYNC の enum で(初期値も OFF)、そのインデックス順は上記のモード順と一致します。この拡張機能の -token-roles トークンは vsql_role_granted と vsql_role_denied をステージングしますが、以下のアカウントはどちらも保持していません。設定がオフの場合、ログインはアカウントのロールをそのままにします:
vsql-auth-test-token-roles を使って auth_user として接続し、SELECT CURRENT_ROLE() で何が有効かを確認します:
SHOW GRANTS はサーバーが追加した付与を表示します:

