vsql_allow_preview_extensions = ON 才能安装(请参阅启用预览层)——不使用预览功能的扩展程序无论此设置如何都会正常安装。
启用预览层
在安装任何使用预览功能的扩展程序之前,使用SET PERSIST 设置 vsql_allow_preview_extensions = ON:
SET GLOBAL 会被拒绝——服务器需要 SET PERSIST,以便设置在重启后仍然有效。具有预览功能的扩展程序在启动时加载,因此在服务器启动时,该变量必须为 ON。
如果您直接启动 mysqld(例如,从安装脚本中启动服务器),请在命令行中传递该标志,而不是使用 mysqld-auto.cnf(因为此时 mysqld-auto.cnf 尚未存在,无法保存持久化值):
功能索引
注册模式
要使用预览功能,请在文件范围内声明一个功能对象,并将其按引用传递给make_extension() 中的 .with()。服务器将在注册期间填充对象的 abi 指针:
.with(capability) 告诉服务器扩展程序需要哪些功能。如果扩展程序在安装时 vsql_allow_preview_extensions 为 OFF,则服务器会拒绝安装,并显示一个包含功能名称的错误。
密钥环访问
密钥环功能 (vsql::preview::keyring) 允许扩展程序读取和写入存储在 MySQL 密钥环组件中的密钥。扩展程序可将其用于 API 密钥、加密密钥或其他不应存储在 SQL 表中的密钥。
功能名称 VEF_PREVIEW_KEYRING_NAME 为 "vsql::preview::keyring"。
为了使读取和写入成功,必须在 MySQL 服务器上安装密钥环组件。如果没有,则操作将返回 KeyringCapability::Status::UNAVAILABLE。
状态值
KeyringCapability::Status 是一个作用域枚举,由 read()(在 ReadResult 中)和 write() 返回:
声明功能
包含头文件,在文件范围内声明一个功能对象,并将其传递给.with():
g_keyring 对象。如果未安装密钥环组件,则 read() 和 write() 方法在运行时将返回 Status::UNAVAILABLE——请在每次调用时检查该状态,而不是基于单独的可用性探测。
读取和写入
data_id 是密钥标识符。auth_id 是所有者用户——传递一个空字符串(或在 read 上省略它,read 默认使用 {})以读取或写入与特定用户无关的内部密钥。
read 返回一个 ReadResult。使用结构化绑定将其绑定:
Status::OK 除外),value 都是空的。
write 直接返回 Status,并将 data 存储在 data_id / auth_id 下。
完整示例
这是与服务器一起提供的vsql_keyring_reader 测试扩展程序的简化版本。它注册了 2 个 VDF:keyring_read 和 keyring_store。
状态变量
status_var 功能 (vsql::preview::status_var) 允许扩展程序将 long long 和 double 计数器作为 MySQL 状态变量公开。扩展程序拥有存储空间并写入其中;服务器每次查询状态变量时,都会通过指针读取。
使用 vsql::preview_status_var::make_capability() 构建功能,并传递来自 make_int(name, value_ptr) 或 make_double(name, value_ptr) 的大括号列表。模板从大括号列表中推断计数,因此不需要显式大小。
完整示例
make_int 需要一个 long long *;make_double 需要一个 double *。这是支持的两种类型。
从 SQL 访问
在INSTALL EXTENSION my_ext 之后,该变量将以扩展程序名称作为前缀显示:
++)可能会偶尔丢失;这对于通过 SHOW STATUS 公开的近似调用计数器是可以接受的。
系统变量
sys_var 功能 (vsql::preview::sys_var) 允许扩展程序注册由扩展程序拥有的存储空间支持的 MySQL 系统变量。支持三种类型:BOOL (bool *)、INT (long long *) 和 STR (char **)。INT 描述符还携带 min_val 和 max_val 边界;所有描述符都携带默认值和注释。
使用 vsql::preview_sys_var::make_capability() 和相应的工厂函数 make_bool、make_int 和 make_str 构建功能。功能对象还公开 get() 和 set(),以便从扩展程序代码中进行编程访问。两者都返回 false 表示成功。
要响应值更改,请在描述符上链接 .on_change<&fn>()。回调将接收一个 sv::SysVarChange,其中包含 var_name() 和类型化的访问器 (as_int()、as_real()、as_str())。
功能对象必须具有静态存储期。当用户设置变量时,MySQL 会直接写入存储指针。
完整示例
从 SQL 访问
安装INSTALL EXTENSION my_ext 后,可以使用扩展名作为组件前缀来访问变量:
从扩展代码读取和写入
对于 INT 和 BOOL 变量,直接读取全局存储指针——MySQL 以原子方式更新这些变量。要通过 MySQL 更新变量(以便服务器处理锁定、范围验证和持久性),请调用SYS_VARS.set(extension_name, var_name, scope, value)。set 和 get 都成功时返回 false。
scope 参数控制持久性:
线程工作器
线程工作器功能 (vsql::preview::thread_worker) 允许扩展在服务器驱动的后台线程中运行。线程通过控制系统变量进行启动和停止,该变量由服务器在扩展加载时注册;服务器在定期计时器上、在文件描述符准备就绪时,或响应启用/禁用事件时调用扩展的工作函数。
功能名称 VEF_PREVIEW_THREAD_WORKER_NAME 是
"vsql::preview::thread_worker"。
声明功能
包含头文件,在文件范围内声明一个实例化了工作函数的ThreadWorkerCapability,并将其传递给 .with():
ThreadWorkerCapability<&my_work>),因此它必须是一个具有以下签名的函数。第一个构造函数参数是线程名称后缀;可选的第二个参数覆盖控制系统变量名称。
工作函数签名
reason 指示服务器调用该函数的原因。thread 是服务器拥有的此工作器的句柄(在初始 VEF_WAKEUP_ENABLE 调用时为 NULL——参见下文)。arg 是在描述符上注册的不透明指针;它会保持不变地传递。
唤醒生命周期
服务器使用以下四个原因之一调用工作函数:
当原因是
VEF_WAKEUP_ENABLE 时,thread 参数为 NULL,因为此时线程句柄尚不存在。对于其他三个原因,thread 不为 NULL。
唤醒返回值
vef_next_wakeup_t,以更新下一个唤醒配置。任何字段中的零值都表示“保持当前设置”——返回一个值初始化的结构体 (return {};) 以保持两者不变。
要设置新的轮询文件描述符,请返回其值(必须大于零)。要清除现有的轮询文件描述符,请在 poll_fd 中返回 -1。
当原因是 VEF_WAKEUP_DISABLE 时,返回值将被忽略。
线程名称和控制变量
描述符上的两个字段控制命名:suffix——线程名称后缀。服务器将其与扩展名称连接起来,生成线程名称,如my_ext/monitor。var_name——可选。如果非 NULL,服务器将注册此确切名称作为控制系统变量。如果为 NULL,服务器将使用默认模式{suffix}_enabled。
SET GLOBAL {suffix}_enabled = ON 启用工作器;将其设置为 OFF 以停止它。
完整示例
一个带有单个周期性工作器的最小扩展,该工作器在每个计时器周期中递增一个心跳计数器。vsql_allow_preview_extensions = ON)后,服务器将注册一个 heartbeat_enabled 系统变量。使用以下命令启用工作器:
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。查询可以以两种模式运行:
execute()——运行该语句并将完整的结果集缓冲到Result中。通过按调用方自己的速度调用next()来迭代行。for_each(fn)——运行该语句,并为生成的每一行调用一次fn,而不进行缓冲。返回的Result仅包含诊断信息(不包含行)。
Result。非 NULL 的 Result 并不意味着该语句已成功——调用 has_error() 以确定。
缓冲 (execute):
column_str() 返回一个 string_view,它仅在下一次 next() 调用或 Result 销毁之前有效。如果需要更长的生命周期,请复制它。string_view 具有 data() == nullptr 表示 SQL NULL。
流式 (for_each):
Row 仅在调用期间有效——不要存储对它的引用,以便跨行使用。for_each 返回的 Result 不包含缓冲的行;在其上调用 next() 不会产生数据。仅将其用于 has_error()、error()、warning_count() 和 warning(i)。
诊断
execute() 和 for_each() 都通过返回的 Result 提供诊断信息。一个诊断信息是一个 Diag:
Result 暴露:
error() 在语句成功时返回一个默认构造的 Diag (errno_ == 0)。warning(i) 在 i >= warning_count() 时返回一个默认构造的 Diag。
sqlstate 和 message 视图指向由 Result 拥有的存储,并在 Result 销毁时失效——如果需要超出其生命周期,请复制它们。
完整示例
一个工作线程,在每个周期执行一个带缓冲的查询和一个流式查询,并记录来自两者的诊断信息:列存储
列存储允许扩展直接向 InnoDB 注册自定义的磁盘布局,用于其自定义类型之一,而不是将该类型的数据通过行的 VARBINARY 负载进行路由。当您的类型需要一种 VARBINARY 无法表达的磁盘布局时,请使用它——例如,必须存储在专用页中的打包浮点数组。这是一个功能特性:它启用了新的存储布局,而不是用于现有布局的调整旋钮。声明功能
两个预览功能协同工作:vsql::preview::storage— 开放对 InnoDB 存储基础设施的访问(微事务、段、页)。在文件范围内声明一个StorageCapability。vsql::preview::column_store— 将每个类型的存储实现绑定到扩展的自定义类型之一。在文件范围内使用make_column_store<Ctx>(TYPE).…build()声明一个ColumnStoreCapability。
make_extension() 上的 .with():
make_column_store<MyCtx>(MY_TYPE) 将实现绑定到在同一扩展上注册的一个自定义类型。所有七个槽位都必须在 build() 时提供,因为每个槽位都映射到列生命周期中的一个不同点,InnoDB 在正常操作期间会到达该点。
七个存储函数
每个函数都接受storage::Column::StorageCtx<MyCtx>*,其 user() 访问器返回扩展的每个列状态,并且其 arena() 提供服务器管理的辅助对象分配。每个函数在成功时返回 false,在出错时返回 true,并将消息写入 error_msg(容量 error_msg_len),以便将故障传递到 SQL 客户端。
mark_delete 和 purge 是不同的,因为 InnoDB MVCC 要求已删除的行在 purge 运行之前对旧的快照保持可读。
每个列的上下文和 Arena
C++ SDK 在调用create 或 load 之前,默认构造 MyCtx——在进入您的函数时,ctx->user() 已经填充。MyCtx 必须是默认可构造的;C++ SDK 使用不带参数的 T() 调用。
直接使用 ctx->user() 来初始化状态。不要调用 ctx->arena().construct<MyCtx>()——这将分配第二个、未使用的实例,并且 ctx->user() 不指向它。
load 遵循相同的模式——ctx->user() 预先填充,并且 storage_ref 携带由 ctx->set_ref() 在 create 中存储的打包值:
ctx->arena() 来分配辅助对象,这些对象太大或太动态,无法直接嵌入到 MyCtx 中。C++ SDK 在 drop 返回后,无论 drop 是否成功,都会自动销毁 arena 并调用 ~MyCtx()。
InnoDB 访问实用程序
包含<villagesql/preview/storage_api.h> 以获取 InnoDB 原语。所有页面的读取和写入都必须在微事务中进行:
create 时保留——有关完整的设置模式,请参见上面“每个列的上下文”中的 create 和 load 示例。在 DML 操作期间,从根页面获取段引用以分配新页面:
mtr_ref 传递给写入调用,以便 InnoDB 记录更改:
在标头或尾部区域内读取或写入会损坏页面——InnoDB 使用这些字节范围用于其自身的簿记和校验和。
语句事件
语句事件功能 (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 测试扩展的精简形式,该扩展与服务器一起提供。它记录每个执行时间超过阈值的查询,将语句事件功能与系统变量结合以进行运行时配置:

