Skip to main content
本指南是编写 C++ VDF 实现的深度参考。它是 使用 C++ 创建扩展 的补充,后者涵盖端到端的构建步骤;也是 C++ 测试 的补充,后者涵盖测试与迭代循环。
VEF 协议 3 自 v0.0.4 版本起已稳定。协议 4 正在开发中,并且仅通过选择加入的开发 ABI 标头提供 (-DVSQL_USE_DEV_ABI=ON)。使用旧协议 2 构建的扩展会被服务器拒绝,必须重新构建。

编写扩展函数

扩展函数是用 C++ 编写的,并向 VEF 注册。包含一个标头以访问完整的 SDK:

参数和结果类型

VDF 参数和结果作为类型安全的参数和结果类型传递。框架会从您的函数签名中检测它们并自动进行调整——make_func 注册语法保持不变。 参数类型: IntArgRealArgStringArgCustomArg——每个都提供 is_null()value()。对于参数化的自定义类型,CustomArgWith<P> 添加了一个 params() 访问器,该访问器返回缓存的解析参数结构(请参阅 参数化类型)。 结果类型: IntResultRealResultStringResultCustomResult——每个都提供 set_null()warning(msg)error(msg)。标量结果还提供 set(value)。缓冲区结果提供 buffer()set_length(len)StringResult 此外还提供 set(std::string_view),它从视图中复制最多 buffer().size() 个字节,并在一次调用中设置长度。对于参数化的自定义类型,CustomResultWith<P> 添加了一个 params() 访问器。 Span 类型: 面向字节的参数和结果类型上的 value()buffer() 返回一个 vsql::Span<T>——一个非所有权视图,用于连续的 T 序列,具有 data()size()empty()begin()/end()operator[]。在 C++20 中,它是 std::span<T> 的别名;在 C++17 中,此 SDK 提供了一个最小的兼容实现,因此相同的代码可以在任何标准中编译。它通过 <villagesql/vsql.h> 提供。 warning(msg) 会为该行返回 SQL NULL,并附加一个 SQL 警告。在严格模式 (STRICT_TRANS_TABLES) 下,MySQL 会将其升级为 INSERT/UPDATE 上的语句错误,因此它在严格上下文中表现得像 error(msg)。将其用于可恢复的错误输入,例如编码函数中的无法解析的字符串。对于已损坏的存储数据或任何继续操作不安全的情况,请使用 error(msg)。两种情况下的消息都会被截断以适应服务器的内部错误缓冲区(如果需要)。 标量示例——将两个整数相加:
二进制示例——就地转换自定义类型缓冲区:
对于 StringResultCustomResult,写入 buffer(),然后调用 set_length(),其中包含写入的字节数。buffer().size() 是最大容量。 对于返回自定义类型的 VDF (returns(CUSTOM(MYTYPE))),服务器会自动调整结果缓冲区的大小以匹配解析的返回类型的 persisted_length——扩展作者在此情况下不需要在函数生成器上声明 .buffer_size(...)。如果 prerun 进一步增大缓冲区,则会保留更大的大小。这使得,例如,SVECTOR::from_string('[…1024 floats…]') 可以在不使结果缓冲区耗尽空间的情况下编码一个大型向量。 您可以在同一扩展中的不同函数中使用不同的样式——每个函数的样式由其自身的签名决定。

聚合 VDF

聚合 VDF 在每个 GROUP BY 组内的行中累积状态,并为每个组返回单个结果,类似于 SQL SUMCOUNT。使用 make_aggregate_func<State, &result_fn>("name") 注册一个。State 类型是每个组的累积缓冲区;prerunpostrun 是自动生成的,用于分配和删除它。 结果函数必须具有签名 void(const State&, ResultType),其中 ResultTypeIntResultRealResultStringResultCustomResultCustomResultWith<P> 中的一个。调用 out.set(value) 以返回一个值,或者调用 out.set_null() 以返回 SQL NULL。 .clear<>().accumulate<>() 都是必需的。生成器在编译时(通过 build())强制执行这一点,并且服务器在 INSTALL EXTENSION 时再次验证它——clear 重置状态,accumulate 折叠行,结果函数读取最终状态。
生成器方法的工作方式:
  • make_aggregate_func<State, &result_fn>() 自动生成 prerunpostrun(值初始化并删除 State)。
  • .clear<&fn>() 注册您的 void(State&) 重置函数。
  • .accumulate<&fn>() 注册您的 void(State&, TypedArgs...) 折叠函数。TypedArgs 从函数签名中推断出来(IntArgStringArg 等)。
  • 结果类型 (IntResultRealResult 等)从结果函数签名中推断出来。
对于永不返回 NULL 的计数器,请使用纯状态类型:
StringResult 聚合 VDF 返回文本:结果报告 utf8mb4_bin 字符集和排序规则,因此客户端将其显示为字符而不是十六进制——与标量 VDF STRING 路径相同。它还以相同的方式支持 .max_result_length(n),调整物化聚合结果(GROUP BY/DISTINCT 临时表、CREATE TABLE ... SELECT 或 UNION)的大小,使其不会在参数宽度处被截断。有关调整大小的规则和上限,请参阅 自定义缓冲区大小

每个语句的状态(Prerun 和 Postrun)

某些 VDF 需要跨单个查询触及的每一行都存在的状态——调用计数器、缓存结果、打开的资源。在 prerun 钩子中分配它,在 VDF 主体中访问它,并在 postrun 钩子中释放它。这两个钩子都为每个语句运行一次;VDF 主体为每一行运行一次。 使用 .prerun<&Hook>().postrun<&Hook>() 注册它们。所需的签名是: 使用 PrerunResult::set_user_data(void*) 来存储状态;使用 PostrunArgs::delete_state<T>() 来释放它。如果 prerun 调用 set_user_data(new T{}),则 postrun 必须调用 delete_state<T>()——SDK 不会自动释放。 PrerunArgs::type_at(i) 公开了在读取任何行之前声明的每个参数的 SQL 类型;返回的 PrerunArgType 上的谓词 is_int()is_real()is_str()is_custom() 镜像列类型。在 prerun 中使用它来验证参数类型或调用 PrerunResult::request_buffer_size(n) 以调整结果缓冲区的大小。

可变参数 VDF

varargs VDF 接受任意数量的任何 SQL 类型的参数。使用 .varargs() 在函数生成器上声明一个,这与 .no_params().param(TYPE) 互斥。主体接收一个 vsql::VarArgs 参数,而不是通常的固定参数个数的参数类型。
Varargs 注册需要 VEF 协议 3。旧服务器会在安装时拒绝该扩展。
框架无法验证 varargs VDF 的参数计数或类型。将每个 varargs 注册与 prerun 钩子配对,该钩子在输入无效时调用 PrerunResult::error(),或者调用 PrerunResult::request_buffer_size(n) 以调整结果缓冲区的大小。 使用 range-for 循环遍历参数。每个 AnyArg 元素都需要在读取其值之前进行类型检查: 在任何访问器之前检查 is_null()——所有四个访问器在 null 参数上都是未定义的。

VEF_GENERATE_REGISTRATION

VEF_GENERATE_REGISTRATION 创建一个内部 _vef_do_register() 辅助函数,该函数执行扩展注册,但不定义 extern "C" 入口点。当您需要自定义 vef_register 行为时使用它——例如,在测试构建中注册后修补描述符。对于常规扩展,请改用 VEF_GENERATE_ENTRY_POINTS

自定义类型操作

有关完整的类型操作生成器参考——编码、解码、比较、哈希、内建默认值和参数化类型——请参阅 类型操作

预览功能

以下 VEF 功能作为选择加入的预览标头提供。ABI 和 API 仍在积极开发中;有关完整参考,请参阅 预览功能

检查扩展注册元数据

INFORMATION_SCHEMA.EXTENSION_REGISTRATION 将每个已加载扩展的内存 VEF 注册结构作为 JSON 文档公开。使用它来验证服务器在 INSTALL EXTENSION 之后是否已正确解析扩展的函数、类型和系统变量。

另请参阅