<villagesql/vsql.h> 提供):
固定长度类型
vsql::make_type<kTypeName>() 注册这些操作。类型名称作为非类型模板参数 (NTTP) 传递——一个 static constexpr const char[]
数组。构建器会根据这个 NTTP 以 TYPE::method 格式(例如 "MYTYPE::from_string")自动生成 VDF 名称,因此无需手动匹配字符串。将构建好的类型对象传递给扩展构建器上的 .type();类型操作不需要单独的 .func() 调用。
from_string、to_string 或 compare,build() 将在编译时失败。每个模板方法都通过 static_assert 检查函数指针签名。
内建默认值
当NOT NULL 自定义类型列在 IGNORE 模式下接收到 NULL 时(例如 INSERT IGNORE 或 UPDATE IGNORE),服务器会调用内建默认值来生成一个回退值,而不是引发错误。内建默认值提供一个字符串表示;服务器使用该类型的 from_string 函数将其转换为二进制。
如果您同时省略 对于固定长度类型,默认字符串必须编码为恰好
.intrinsic_default_str() 和 .intrinsic_default_vdf(),服务器会调用 from_string("") 作为回退。这发生在该类型首次使用时(在创建表时),而不是在 INSTALL EXTENSION 时。如果您的编码函数拒绝空字符串——或者将其编码为错误的字节数——类型初始化将失败,并在 SQL 客户端中显示一个错误:persisted_length 个字节。对于任何空字符串不是有效输入的类型,请设置一个显式默认值。.intrinsic_default_str()
对于常量默认值,请直接在类型构建器上传递字符串(如上面固定长度示例中的 .intrinsic_default_str("0") 所示)。
基于 VDF:.intrinsic_default_vdf() + make_intrinsic_default
当默认值依赖于类型参数时,请针对以下签名之一实现一个函数(通过 <villagesql/vsql.h> 提供):
std::string 表示。出错时,向 error_msg 写入一条消息并返回任意值(SDK 通过检查 error_msg[0] != '\0' 来检测错误)。使用 make_intrinsic_default<&fn>("vdf_name")(一个参数:VDF 名称)进行注册,并在类型构建器上通过 .intrinsic_default_vdf() 引用该名称。下面的参数化类型示例展示了完整的注册模式。
参数化类型
参数化类型需要在编码、解码、比较和哈希时获取列声明的参数,以确定分配大小和布局。定义一个带有解析函数和逆向to_strings 函数的参数结构体,在类型构建器上通过 .params<P, &ParseFunc, &ToStringsFunc>() 注册两者,并将 const P& 用作类型操作函数的第一个参数。SDK 会为每个唯一的参数组合缓存解析结果,因此解析函数每个类型实例化最多运行一次。to_strings 函数是 parse 的逆函数:它将一个类型化的 P 写回规范的键/值字符串形式,以便服务器可以按照 parse 消费的相同形状发布推断出的参数。
.params<>()。使用 .int_to_params<&mytype_int_to_params_fn>()
处理 MYTYPE(N) 整数语法,使用 .resolve_params<&mytype_resolve_params_fn>() 来验证参数并计算存储大小。调用 .max_persisted_length(N) 并传入所有有效参数化下持久化字节大小的上界;服务器仅在类型参数推断路径上使用它,此时它尚未推断出参数,因此无法查询 resolve_params 来确定编码缓冲区的大小。对于基于 VDF 的内建默认值,请使用带有 VDF 名称的 .intrinsic_default_vdf(),并通过 make_intrinsic_default<&mytype_default>() 单独注册该 VDF。
TypeEncodeWithParamsFunc<P>、
TypeDecodeWithParamsFunc<P>、TypeCompareWithParamsFunc<P> 和
TypeHashWithParamsFunc<P>——以及 ParamsToStringsFunc<P>
(void fn(const P&, std::map<std::string,std::string>&)) 均通过
<villagesql/vsql.h> 提供。
vsql::make_type 模板方法会检测 params 参数并自动通过 params 缓存进行路由。编码函数将
vsql::MaybeParams<P> & 作为第一个参数;is_known() 在运行时始终为 true,value() 返回 const P&。解码、比较和哈希变体接受
vsql::CustomArgWith<P>,其 params() 访问器返回 const P&。
在 SQL 中提供参数。 有两种语法可以到达 resolve_params:
- 整数——
MYTYPE(N)。服务器将N通过int_to_params路由以构建参数映射。需要.int_to_params<>()。 - 字符串——
MYTYPE('key=value,...')。服务器规范化该字符串并直接调用resolve_params;不涉及int_to_params。只要注册了.resolve_params<>()即可使用——无需额外的构建器调用。
.resolve_params<>() 的类型接受字符串形式并拒绝 MYTYPE(N)。SHOW CREATE TABLE 会保留写入时所用的形式。
int_to_params 生成并由 resolve_params 消费的序列化 key=value,... 参数字符串上限为 VEF_MAX_TYPE_PARAMS_STRING_LEN
(1024 字节)。如果某个参数化配置的规范字符串会超过该限制,将被拒绝并返回一个明确定义的错误,而不是被静默截断——请将单个类型的参数名称与值的组合保持在 1024 字节以内。重写参数并提供默认值
resolve_params 还有第二个会修改参数的重载:它以非常量引用的方式接收参数映射,以便类型可以重写它——通常是为了填充作者省略的默认值。以相同的方式注册它(.resolve_params<&fn>() 接受任一形式;只注册其中一个):
SHOW CREATE TABLE 打印的规范参数字符串,因此重写必须是幂等的。裸声明(MYTYPE,无长度或参数)现在会以一个空映射调用 resolve_params,而不是跳过它,因此提供默认值的类型会为每一列都赋予显式参数——vsql_bitfield_test 的 BITFIELD 会将一个裸列解析为 max_number_of_bits=4096:
可变长度类型
可变长度自定义类型按每个值决定其持久化大小,而不是使用单一的固定占用空间。通过在类型构建器上调用.variable_length_type() 来声明一个可变长度类型,该调用会设置类型的 variable_length 标志。
可变长度类型还必须调用 .max_persisted_length(N)。如果省略它,build() 将在编译时失败——服务器需要这个上界来为底层字段分配缓冲区。
.variable_length_type() 是单调的:在协议 3 的设置器(max_persisted_length()、params()、int_to_params())之前或之后调用它,都不会将协议要求降回到协议 4 以下。
与每个自定义类型一样,可变长度类型必须生成一个可用的
内建默认值。默认值被编码到字段的最大容量中,并且任何 1 到
否则,当一个
max_persisted_length 字节之间的非空结果都会被接受。如果某个类型对空字符串编码的结果为零字节——比如空数组或空位集——它就没有可用的默认值,因此请声明一个编码结果非空的显式默认值:NOT NULL 列首次引用该类型时(在 CREATE TABLE 处),该类型将无法初始化——这与无法编码 from_string("") 的固定长度类型相同。存储过程中的自定义类型
自定义扩展类型可用作存储过程的参数类型,也可用于DECLARE 变量声明中。服务器在例程执行时使用已安装扩展的类型元数据来解析自定义类型。
另请参阅
- C++ 中的自定义类型 — 自定义类型的入门介绍
- C++ API 参考 — VDF 契约、空值处理和缓冲区大小调整
- C++ 开发 — VDF 编写深度、参数和结果类型、聚合、可变参数

