- 大数据
- 数据分析
- 后端
【免费下载链接】datafusion
Apache DataFusion SQL Query Engine
Apache DataFusion 是一个用 Rust 编写的可扩展查询执行框架,使用 Apache Arrow 作为内存数据格式。在 DataFusion 中,内置的标量函数、聚合函数和窗口函数并非"散落在文档里",而是通过datafusion-doc子模块提供的结构化类型与过程宏,在源码中就地声明、再由工具自动生成官方 SQL 函数文档。本文将以仓库中的 datafusion/doc/README.md 为核心骨架,深入datafusion-doccrate 的Documentation/DocSection/DocumentationBuilder结构、#[user_doc]声明式宏以及整套文档生成管线,读完你将掌握如何为自己的 UDF 编写能被官方文档机制识别、渲染的规范文档。
一、datafusion-doc 是什么:UDF 文档化子模块的定位
按照 datafusion/doc/README.md 的说明,DataFusion 是一个用 Rust 编写的可扩展查询执行框架(extensible query execution framework),并将 Apache Arrow 作为内存格式(in-memory format)。datafusion-doc这个 crate 是 DataFusion 的一个子模块,它的职责非常聚焦:
This crate is a submodule of DataFusion that provides structures and macros for documenting user defined functions.
即:提供用于记录(文档化)用户自定义函数(UDF)的数据结构与宏。这里的 UDF 涵盖三类:标量函数(ScalarUDFImpl)、聚合函数(AggregateUDFImpl)和窗口函数(WindowUDFImpl)。
从 datafusion/doc/Cargo.toml 可以看到该模块的包名为datafusion-doc、库名为datafusion_doc,描述为 "Documentation module for DataFusion query engine"。
README 还给出了一个重要的使用建议:
Most projects should use the
datafusioncrate directly, which re-exports this module. If you are already using thedatafusioncrate, there is no reason to use this crate directly in your project as well.
也就是说,datafusion-doc是 DataFusion 内部模块化拆分的产物,对外并不需要用户单独依赖。在仓库内部,datafusion/core/src/bin/print_functions_docs.rs 直接以use datafusion_expr::{DocSection, Documentation, aggregate_doc_sections, scalar_doc_sections, window_doc_sections, ...}的方式引用这些符号,说明它们经由datafusion_expr再导出、并最终随datafusion主 crate 一并暴露给使用者。因此,应用开发者只要依赖datafusion主 crate,就能拿到完整的 UDF 文档化能力。
二、核心数据结构:Documentation / DocSection / DocumentationBuilder
datafusion-doc的全部核心类型都定义在 datafusion/doc/src/lib.rs 中,由三条主线组成:最终产物Documentation、分区标记DocSection、以及构建器DocumentationBuilder。
2.1 Documentation:一份 UDF 文档的完整描述
Documentation结构体(lib.rs)承载了单个 UDF 的全部文档信息,字段含义如下:
| 字段 | 类型 | 含义 |
|---|---|---|
doc_section | DocSection | 该 UDF 在文档中归属的分区(例如 "Math Functions") |
description | String | 函数的功能描述 |
syntax_example | String | 语法示例,如"ascii(str)" |
sql_example | Option<String> | 一段 SQL 示例,通常以 SQL 提示符查询与输出形式给出;除最简单的函数外强烈建议提供 |
arguments | Option<Vec<(String, String)>> | 参数列表,按添加顺序展示,二元组左侧为参数名、右侧为参数描述 |
alternative_syntax | Option<Vec<String>> | 该函数其它可用的语法写法 |
related_udfs | Option<Vec<String>> | 相关函数名列表,值必须与相关 UDF 的 name 完全一致 |
值得注意的是,DataFusion 官方的 SQL 函数文档正是由这些结构体自动生成的("The DataFusion SQL function documentation is automatically generated from these structs"),UDF 的名称取自ScalarUDFImpl::name、AggregateUDFImpl::name或WindowUDFImpl::name。
2.2 DocSection:把函数归入正确的文档分区
DocSection(lib.rs)用于指定文档中的展示分区,包含三个字段:
include:是否将该分区包含进公开文档(true包含,false不包含);label:展示标签,例如"Math Expressions";description:可选的分区级描述。
其Default实现(lib.rs)返回include: true、label: "Default"、description: None,适合那些不出现在 DataFusion 官方文档中的自定义 UDF——即默认可见,但没有专属分类。
2.3 DocumentationBuilder:链式构建文档
DocumentationBuilder采用经典的 builder 模式(lib.rs),通过Documentation::builder(doc_section, description, syntax_example)创建,随后以链式方法补齐可选信息:
| 方法 | 作用 |
|---|---|
with_doc_section | 覆盖文档分区 |
with_description | 覆盖功能描述 |
with_syntax_example | 覆盖语法示例 |
with_sql_example | 添加 SQL 示例 |
with_argument(name, description) | 追加一个参数说明(按添加顺序展示) |
with_standard_argument(name, expression_type) | 追加一个标准"表达式"参数说明,自动拼接固定的模板文案 |
with_alternative_syntax(syntax) | 追加一种替代语法 |
with_related_udf(name) | 追加一个相关函数 |
build() | 构建出最终的Documentation(doc_section、description、syntax_example未设置时会 panic) |
with_standard_argument会生成统一风格的参数描述:传入Some("String")时渲染为 "String expression to operate on. Can be a constant, column, or function, and any combination of operators.",传入None时则以 "The" 开头渲染同样的模板,保证文档中大量通用参数表述一致。
lib.rs自带的 doctest 展示了最小可用写法:
use datafusion_doc::{DocSection, Documentation}; let doc_section = DocSection { include: true, label: "Display Label", description: None, }; let documentation = Documentation::builder(doc_section, "Add one to an int32".to_owned(), "add_one(2)".to_owned()) .with_argument("arg_1", "The int32 number to add one to") .build();三、预定义文档分区:三种 UDF 各自可用的 DocSection
为了让内置函数在官方文档中归类统一,datafusion-doc在三个子模块中预定义了各类型 UDF 的标准分区。#[user_doc]宏(见下文)会按label查找并匹配这些预定义分区。
3.1 标量函数分区(scalar_doc_sections)
定义于 datafusion/doc/src/udf.rs,共 12 个分区:
- Math Functions
- Conditional Functions
- String Functions
- Binary String Functions
- Regular Expression Functions(自带分区描述,说明 DataFusion 使用 PCRE-like 正则语法,支持
i、m、s、R、U等可选标志) - Time and Date Functions
- Array Functions
- Struct Functions
- Map Functions
- Hashing Functions
- Union Functions(自带描述,强调 union 数据类型即 tagged unions / variant types / enums / sum types,且与 SQL 的 UNION 运算符无关)
- Other Functions
3.2 聚合函数分区(aggregate_doc_sections)
定义于 datafusion/doc/src/udaf.rs,共 3 个分区:General Functions、Statistical Functions、Approximate Functions。
3.3 窗口函数分区(window_doc_sections)
定义于 datafusion/doc/src/udwf.rs,共 3 个分区:
- Aggregate Functions(自带描述 "All aggregate functions can be used as window functions.")
- Ranking Functions
- Analytical Functions
四、声明式文档:#[user_doc]过程宏
手写DocumentationBuilder链式代码仍然繁琐,因此 DataFusion 在datafusion-macros中提供了#[user_doc(...)]过程宏(datafusion/macros/src/user_doc.rs),把文档写成紧邻函数结构的声明式属性,再由宏在编译期自动生成DocumentationBuilder调用代码。
宏支持的属性项包括:doc_section(label = "...")、description = "..."、syntax_example = "..."、sql_example = r#"... "#、standard_argument(name = "...", prefix = "...")、argument(name = "...", description = "...")、alternative_syntax = "..."、related_udf(name = "...")。其中doc_section会尝试按 label 查找预定义的DocSection。
以真实的内置函数coalesce为例(datafusion/functions/src/core/coalesce.rs):
#[user_doc( doc_section(label = "Conditional Functions"), description = "Returns the first of its arguments that is not _null_. Returns _null_ if all arguments are _null_. This function is often used to substitute a default value for _null_ values.", syntax_example = "coalesce(expression1[, ..., expression_n])", sql_example = r#"```sql > select coalesce(null, null, 'datafusion'); +----------------------------------------+ | coalesce(NULL,NULL,Utf8("datafusion")) | +----------------------------------------+ | datafusion | +----------------------------------------+ ```"#, argument( name = "expression1, expression_n", description = "Expression to use if previous expressions are _null_. Can be a constant, column, or function, and any combination of arithmetic operators. Pass as many expression arguments as necessary." ) )] #[derive(Debug, PartialEq, Eq, Hash)] pub struct CoalesceFunc { pub(super) signature: Signature, }宏的文档注释(user_doc.rs)说明了它展开后生成的代码形态:为结构体生成fn doc(&self) -> Option<&datafusion_doc::Documentation>方法,内部用std::sync::LazyLock缓存一个Documentation::builder(...)链式调用的结果,例如:
fn doc(&self) -> Option<&datafusion_doc::Documentation> { static DOCUMENTATION: std::sync::LazyLock<datafusion_doc::Documentation> = std::sync::LazyLock::new(|| { datafusion_doc::Documentation::builder( datafusion_doc::DocSection { include: true, label: "Time and Date Functions", description: None, }, "Converts a value to a date (`YYYY-MM-DD`).".to_string(), "to_date(expression[, ..., format_n])".to_string(), ) .with_sql_example(...) .with_standard_argument("expression", "String".into()) .with_argument("format_n", ...) .build() }); Some(&DOCUMENTATION) }也就是说,#[user_doc]只是声明式语法糖,最终仍落到第二节中的Documentation/DocumentationBuilder之上。生成的文档可通过ScalarUDFImpl、AggregateUDFImpl、WindowUDFImpl的documentation()方法取出(见 datafusion/expr/src/udf.rs 及 datafusion/expr/src/udaf.rs、datafusion/expr/src/udwf.rs 中对应的documentation方法)。
五、从结构体到官方 SQL 文档:完整的生成管线
数据最终落地的环节是文档生成器。datafusion/core下提供了print_functions_docs二进制(datafusion/core/src/bin/print_functions_docs.rs),它遍历当前会话注册的全部函数,按aggregate/scalar/window三种类型把每个 UDF 的Documentation渲染成 Markdown 输出到 stdout。
围绕它的自动化脚本是 dev/update_function_docs.sh,执行流程大致为:
- 以仓库根目录为基准,默认输出目录为
docs/source/user-guide/sql,可通过--output-dir DIR覆盖; - 依次生成三个页面,例如聚合函数页使用命令:
cargo run --manifest-path datafusion/core/Cargo.toml --features docs_generation --bin print_functions_docs -- aggregate - 将渲染结果连同固定的文件头写入
docs/source/user-guide/sql/aggregate_functions.md、scalar_functions.md、window_functions.md。
生成的 Markdown 文件头部会明确标注:"This file was generated by the dev/update_function_docs.sh script. Do not edit it manually as changes will be overwritten.",并提示应修改 UDF 的documentation()函数或脚本本身来更新文档。这些页面最终通过 docs/source/user-guide/sql/index.rst 挂入用户指南。
以coalesce为例,docs/source/user-guide/sql/scalar_functions.md 中对应的章节正是由#[user_doc]属性中的描述、语法示例、SQL 示例渲染而来:
> select coalesce(null, null, 'datafusion'); +----------------------------------------+ | coalesce(NULL,NULL,Utf8("datafusion")) | +----------------------------------------+ | datafusion | +----------------------------------------+由此可见:源码中的#[user_doc]是唯一事实来源,print_functions_docs是渲染器,update_function_docs.sh负责把渲染结果落盘为官方文档。
六、编写 UDF 文档的规范与约束
结合lib.rs的文档注释与宏实现,为 UDF 编写文档时有几条明确的规范需要遵守:
- Markdown 格式:
Documentation中所有字符串都必须使用 Markdown 格式书写(lib.rs明确要求),包括描述中的强调、代码与链接。 - 当前仅支持单语言:文档目前只支持一种语言,所有文本应使用英文。
related_udfs必须同类型且名字精确:相关函数的值应与对应 UDF 的name()完全一致,并且必须是同一种 UDF 类型(标量、聚合或窗口),否则无法正确生成互链。doc_section标签需匹配预定义分区:#[user_doc]宏会按label查找预定义的DocSection,因此自定义分区标签时应参考第三节中列出的标准分区集合;对不出现在官方文档中的自定义 UDF,可使用DocSection::default()(include: true、label 为"Default")。- 尽量提供
sql_example:除最简单的函数外,官方强烈建议提供带实际输出结果的 SQL 示例,这能显著提升文档的可读性与可验证性。 - 不要手工编辑生成文件:
docs/source/user-guide/sql下的函数文档由脚本生成,应通过修改 UDF 实现中的文档声明后重新运行 dev/update_function_docs.sh 来更新。
此外,Documentation还提供了一个to_doc_attribute()方法(lib.rs),能把已有的代码式文档结构输出为等价的#[user_doc(...)]属性文本——这是从"代码构建文档"迁移到"属性声明文档"的半自动化辅助工具,文档注释中说明它可用于 UDF 文档生成方式的迁移过渡,迁移完成后即可安全移除。
结语
datafusion-doc虽然只是 DataFusion 仓库中的一个文档子模块,却完整承载了"UDF 文档从声明到发布"的整条链路:Documentation/DocSection/DocumentationBuilder提供结构化数据模型,#[user_doc]宏让文档可以伴随函数实现就地书写,print_functions_docs与update_function_docs.sh则把源码中的声明自动渲染为官方 SQL 函数页面。理解了这套机制,无论是为 DataFusion 贡献内置函数,还是在自己的扩展中维护 UDF 文档,都能做到"一处声明、处处生效",并保证文档与实现永远同源。
- 大数据
- 数据分析
- 后端
【免费下载链接】datafusion
Apache DataFusion SQL Query Engine
相关推荐
Apache DataFusion 的 `user_doc` 过程宏:为 UDF 编写结构化用户文档的完整指南
Apache DataFusion 的 user_doc 过程宏:为 UDF 编写结构化用户文档的完整指南 Apache DataFusion 是一个用 Rus
大数据数据分析后端Apache DataFusion 表函数库解析:generate_series 与 range 的源码实现与实战指南
Apache DataFusion 表函数库解析:generate_series 与 range 的源码实现与实战指南 Apache DataFusion 是一
大数据数据分析后端Apache DataFusion窗口函数错误处理文档:最佳实践
Apache DataFusion窗口函数错误处理文档:最佳实践 窗口函数(Window Function)是Apache DataFusion中处理复杂分析场
大数据数据分析后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考