29 个版本
0.5.5 | 2024年5月7日 |
---|---|
0.5.4 | 2023年9月10日 |
0.5.3 | 2023年3月29日 |
0.5.0 | 2022年12月20日 |
0.3.10 | 2022年11月13日 |
#122 在 数据库接口
135 每月下载量
在 mysql_roaring 中使用
150KB
2.5K SLoC
UDF:Rust 中 MariaDB/MySQL 用户定义函数
本库旨在以最小错误的方式,使 SQL 用户定义函数 (UDF) 的实现变得极其简单。
寻找预先编写的有用 UDF?请查看 UDF 套件,它提供了一些有用函数的可下载二进制文件:https://github.com/pluots/udf-suite。
在此处查看文档:https://docs.rs/udf/latest
UDF 理论
基本的 SQL UDF 包含三个公开函数
- 一个初始化函数,用于检查参数并分配内存
- 一个处理函数,返回一个结果
- 一个析构函数,清理堆上的任何内容(在此库中自动执行)
聚合 UDF(一次处理多行的函数)只需注册两个到三个额外的函数。
此库处理编写 UDF 时所有原本困难的部分(动态注册、分配/释放、错误处理、可空值、日志记录),使得向您的 SQL 服务器实例添加任何函数变得 极其简单。它还包括一个模拟接口,以便在不需要服务器的情况下测试您的函数实现。
快速入门
使用此库创建工作 UDF 的步骤如下
-
创建一个新的 rust 项目(
cargo new --lib my-udf
),将udf
添加为依赖项(cargo add udf
),并将 crate 类型更改为cdylib
,在Cargo.toml
中添加以下内容[lib] crate-type = ["cdylib"]
-
创建一个结构体或枚举,用于在初始化和处理步骤之间共享数据(它可以空)。您的 UDF 的默认名称将是您的结构体名称转换为蛇形命名法。
-
在此结构体上实现
BasicUdf
特性 -
如果您想将其实现为聚合函数,请实现
AggregateUdf
特性。 -
将这些
impl
块添加#[udf::register]
(可选地带有(name = "my_name")
参数) -
使用
cargo build --release
编译项目(输出将位于target/release/libmy_udf.so
) -
使用
CREATE FUNCTION ...
将结构加载到 MariaDB/MySql 中 -
在 SQL 中使用该函数!
有关使用此库编写的某些 UDF 的示例,请参阅 udf-examples/
目录或 udf-suite
存储库。
详细概述
本节将详细介绍使用此库实现 UDF 的细节,但并非详尽无遗。有关详细信息,请参阅文档或 udf-examples
目录中的注释良好的示例。
结构创建
第一步是创建一个结构(或枚举),它将用于在所有相关 SQL 函数之间共享数据。这些包括
init
每个结果集调用一次。在这里,您可以存储结构中的常量数据(如果适用)process
每行调用一次(对于聚合函数,为每组调用一次)。此函数使用结构中的数据和当前行的参数中的数据clear
仅聚合,在每组开始时调用一次。根据需要重置结构。add
仅聚合,在组内的每行调用一次。执行所需的计算并将数据保存到结构中。remove
窗口函数仅,用于从组中删除一个值
对于简单函数,尤其是没有需要共享的数据的情况,这是完全可能的。在这种情况下,只需创建一个空的结构,就不会发生分配。
/// Function `sum_int` just adds all arguments as integers and needs no shared data
struct SumInt;
/// Function `avg` on the other hand may want to save data to perform aggregation
struct Avg {
running_total: f64
}
对于返回缓冲区的函数(字符串和十进制函数):如果字符串长度超过 MYSQL_RESULT_BUFFER_SIZE
(255),则要返回的字符串必须包含在结构中(如果适用,则 process
函数将返回一个引用)。
/// Generate random lipsum that may be longer than 255 bytes
struct Lipsum {
res: String
}
特质实现
下一步是实现 BasicUdf
和可选的 AggregateUdf
特性。有关更多信息,请参阅 文档。
如果您在使用 IDE 时使用 rust-analyzer,它可以帮到您。只需输入 impl BasicUdf for MyStruct {}
并将光标放在括号之间 - 它将提供自动填充函数骨架的选项(如果默认情况下未显示,则 ctrl+.
或 cmd+.
会弹出此菜单)。
use udf::prelude::*;
struct SumInt;
#[register]
impl BasicUdf for SumInt {
type Returns<'a> = Option<i64>;
fn init<'a>(
cfg: &UdfCfg<Init>,
args: &'a ArgList<'a, Init>
) -> Result<Self, String> {
// ...
}
fn process<'a>(
&'a mut self,
cfg: &UdfCfg<Process>,
args: &ArgList<Process>,
error: Option<NonZeroU8>,
) -> Result<Self::Returns<'a>, ProcessError> {
// ...
}
}
编译
假设以上步骤已执行,所需做的只是为该项目生成一个C动态库。这可以通过在您的Cargo.toml
文件中指定crate-type = ["cdylib"]
来完成。之后,使用cargo build --release
编译将会生成一个可加载的.so
文件(位于target/release
)。
重要版本说明:此crate依赖于名为泛型关联类型(GATs)的功能,该功能仅在rust >= 1.65的版本中可用。此版本刚刚稳定(2022-11-03),因此如果您遇到编译器问题,请务必运行rustup update
。
CI在Linux和Windows上都运行测试,因此此crate应适用于任一平台。MacOS尚未经过测试,但可能也能正常工作。
符号检查
如果您想验证是否存在正确的C可调用函数,可以使用nm
检查动态库。
# Output of example .so
$ nm -gC --defined-only target/release/libudf_examples.so
00000000000081b0 T avg_cost
0000000000008200 T avg_cost_add
00000000000081e0 T avg_cost_clear
0000000000008190 T avg_cost_deinit
0000000000008100 T avg_cost_init
0000000000009730 T is_const
0000000000009710 T is_const_deinit
0000000000009680 T is_const_init
0000000000009320 T sql_sequence
...
使用方法
编译完成后,需要将生成的目标文件复制到SQL变量plugin_dir
的位置 - 通常这是/usr/lib/mysql/plugin/
。
完成此操作后,在MariaDB/MySql中可以使用CREATE FUNCTION
来加载它。
Docker使用
强烈建议在Docker中进行测试,以避免干扰主机SQL安装。有关如何操作的说明,请参阅udf-examples的readme。
示例
udf-examples
crate包含了各种UDF的示例以及如何编译它们的说明。有关详细信息,请参阅该readme。
日志记录与调试说明
如果您需要在函数的正常使用过程中记录诸如警告之类的信息,任何打印到stderr
的内容都将出现在服务器日志中(例如,如果使用Docker进行测试,可以通过docker logs mariadb_udf_test
查看)。udf_log!
宏将打印与SQL日志信息格式匹配的消息。您还可以启用crate功能logging-debug
以进行函数入口/退出点调试,或启用logging-debug-calls
以获取来自MariaDB/MySQL服务器的确切调用参数信息。
调试的最佳方法是使用udf::mock
模块创建单元测试。这些测试可以运行以验证正确性,如果需要,也可以使用调试器逐步执行(这种情况可能相对较少)。所有类型都实现了Debug
,因此也可以轻松打印(内置的dbg!
宏将打印到stderr
,因此这也会出现在日志中)。
dbg!(&self);
let arg0 = dbg!(args.get(0).unwrap())
[udf_examples/src/avgcost.rs:58] &self = AvgCost {
count: 0,
total_qty: 0,
total_price: 0.0,
}
[udf_examples/src/avgcost.rs:60] args.get(0).unwrap() = SqlArg {
value: Int(
Some(
10,
),
),
attribute: "qty",
maybe_null: true,
arg_type: Cell {
value: INT_RESULT,
},
marker: PhantomData<udf::traits::Process>,
}
许可证
自版本0.5.1起,此作品采用Apache 2.0和GPL 2.0(或任何后续版本)双重许可。如果您使用此作品,可以选择其中之一。
SPDX-许可证-标识符:Apache-2.0 或 GPL-2.0-或-后续版本
依赖关系
~1.3-2MB
~36K SLoC