VDF 函数契约
这些契约规定了 VDF 实现函数如何与 VEF 运行时交互。通过make_func<> 注册的每个函数都必须遵循这些契约。以下引用的类型可以通过 #include <villagesql/vsql.h> 获取。
A 部分:VDF 函数契约
1. VDF 实现函数是void 类型——它们绝不返回值。
out.set(...) / out.set_length(n)、out.set_null()、out.warning(msg) 或 out.error(msg)。
2. 将 result->type 设置为四个结果常量中的一个。
在 vef_return_value_type_t 中存在四个常量:
没有特定于类型的变体。
VEF_RESULT_VALUE 是字符串、整数、实数和自定义类型等所有类型的单个成功常量。输出类型由函数使用的结果包装器确定(StringResult、IntResult、RealResult、CustomResult)。
3. 在调用 input.value() 之前,检查 input.is_null()。
如果 is_null() 返回 true,则调用 value() 的行为未定义。
out.buffer() 并调用 out.set_length(n)。在写入之前,检查 out.buffer().size()。
out.buffer()返回一个Span<char>,指向服务器管理的缓冲区。out.set_length(n)记录写入的字节数。out.buffer().size()是最大容量。在写入之前,始终检查它。
out.error(msg)。如果需要,消息将被截断为 VEF_MAX_ERROR_LEN(512 字节)。
out.error(msg) 接受一个 std::string_view。它将消息复制到服务器管理的缓冲区中,并在一个调用中将结果状态设置为错误。
实现包装函数
实现函数使用类型化的参数和结果包装器:处理 NULL 值
通过is_null() 检查 NULL,并通过调用 set_null() 返回 NULL:
- 输入 NULL 检查:
input.is_null() - 返回 NULL:
out.set_null() - 返回值:
out.set(v)(数值/自定义)或在写入out.buffer()后调用out.set_length(n)(字符串) - 返回警告:
out.warning(msg)——为该行返回 NULL,添加 SQL 警告,继续执行;在严格模式下,MySQL 会将其升级为 INSERT/UPDATE 上的错误。调用它而不是out.set(),而不是同时调用。 - 返回错误:
out.error(msg)——中止语句执行
错误处理
对于验证失败或无效输入,返回带有自定义消息的错误:VEF_RESULT_VALUE- 成功 (out.set(v)/out.set_length(n))VEF_RESULT_NULL- NULL 值 (out.set_null())VEF_RESULT_WARNING- 行级别警告(返回 NULL,添加 SQL 警告,继续执行;严格模式升级为 INSERT/UPDATE 上的错误)(out.warning(msg))VEF_RESULT_ERROR- 致命错误,中止语句执行 (out.error(msg))
预执行/后执行状态
预执行和后执行钩子使用类型化的包装器。所需的签名是:vef_prerun_args_t* / vef_postrun_args_t*)在 .prerun<&Hook>() 和 .postrun<&Hook>() 中通过 static_assert 在编译时被拒绝。
PrerunArgs 和 PostrunArgs 方法的详细信息,请参阅开发指南中的每语句状态(预执行和后执行)。
大多数扩展不需要预执行/后执行钩子。 VEF SDK 会自动处理常见情况,例如类型检查和结果缓冲区大小调整——对于返回字符串和返回自定义类型的 VDF,结果缓冲区将在 VDF 主体运行之前调整为适合解析后的返回类型。仅当您需要昂贵的每语句设置(例如打开连接),而这些设置不应为每行设置时,才使用预执行/后执行。如果您发现您的用例需要预执行/后执行,请在 VillageSQL Discord 上分享您的场景——团队可能会添加 SDK 支持以自动处理它。
聚合函数
内置的聚合函数 COUNT(DISTINCT)、MIN、MAX 和 GROUP_CONCAT 默认情况下可与自定义类型一起使用。MIN 和 MAX 需要在类型上注册一个比较函数。 还支持自定义聚合 VDF。使用make_aggregate_func<State, &result_fn>("name") 注册一个,然后链接 .returns()、.param()、.clear<>() 和 .accumulate<>(),然后再调用 .build()。.clear<>() 和 .accumulate<>() 都是必需的。有关构建器 API 和回调签名,请参阅聚合 VDF。
与自定义类型一起使用的内置聚合操作:
- 每个函数调用处理具有自己的结果缓冲区(线程安全)的一行
prerun/postrun提供每语句的设置/清理- 避免全局状态——而是使用函数参数和返回值
- 如果必须使用全局状态,请使用互斥锁/锁对其进行保护
窗口函数
以下窗口函数可与自定义类型一起使用:临时表
自定义类型可在临时表中工作。CREATE TEMPORARY TABLE、INSERT 和 ALTER TABLE 的行为与永久表相同。
预览 API
某些 VEF 功能作为 SDK 头文件目录中villagesql/preview/ 下的可选头文件提供。ABI 和 API 仍在积极开发中,可能会在没有事先通知的情况下发生更改。
要选择加入,请将头文件添加到扩展源中。例如:
<villagesql/vsql.h> 包含;您必须直接包含它才能选择加入。
vsql::preview 下的命名空间布局是按功能划分的——没有单一的通用模式。密钥环 API 使用 vsql::preview_keyring::KeyringCapability;线程工作器 API 使用 vsql::preview_thread_worker::ThreadWorkerCapability;SQL 查询 API 使用 vsql::preview_sql_query::SqlQueryCapability,并且必须从后台工作线程句柄 (vef_thread_handle_t *) 打开。请检查每个头文件以获取它定义的精确命名空间和类名。
有关完整的预览 API 文档,请参阅预览功能。
触发器
触发器会在具有自定义类型列的表上触发。触发器主体可以引用NEW 和 OLD 中的非自定义类型列。在触发器主体中访问自定义类型列的值尚未支持。

