拓冰建站拓冰建站
首页 / 资讯中心 / 正文

RisingWave Planner Test 详解:用 YAML 驱动的绑定器、规划器与优化器测试体系

数据库流处理后端数据工程【免费下载链接】risingwaveEvent streaming platform for agentic AI. Continuously ingest, transform, and serve event streams in real time, at scale.项目地址https://gitcode.com/gh_mirrors/ri/risingwave点击查看免费下载导读RisingWave 的planner_test是一个基于 YAML 文件的数据驱动测试框架用于对查询从 SQL 解析、绑定Binder、逻辑规划Planner、优化Optimizer到批量/流式物理计划生成的完整链路做回归验证。阅读本文后你将掌握测试用例的编写格式、expected_outputs各类检查项的语义、输出自动更新的risedev工作流以及如何在本地精准运行单个测试文件。模块概览这个测试工具解决什么问题在 RisingWave 前端src/frontend中一条 SQL 从文本到可执行计划要经过多层转换sqlparser解析为 AST →Binder绑定为逻辑表达式 →Planner产出逻辑计划 →Optimizer做规则优化 → 再分别生成批量batch与流式stream物理计划。任何一层的行为变化都可能导致性能回退或语义错误因此需要一个能够固定各阶段中间产物的测试手段。planner_test模块正是为此而生的工具。它在 src/frontend/planner_test 目录下实现其 README.md 明确说明该模块是面向 binder、planner 与 optimizer 的测试工具——给定一组 SQL 作为输入测试运行器会检查产生的逻辑算子树logical operator tree和物理算子树physical operator tree。整个模块的文件组织如下测试运行入口tests/planner_test_runner.rs一个自定义 harness 的nextest测试测试数据目录tests/testdata输入放在input/子目录期望输出放在output/子目录库代码src/lib.rs 与 src/resolve_id.rs负责解析 YAML、驱动执行与校验输出任务定义planner_test.toml以cargo-make任务形式封装了do-apply-planner-test与run-planner-test两个命令。值得注意的一个设计细节输出目录中的 YAML 文件首行都写着此文件由工具自动生成见 tests/testdata/output/basic_query.yaml 第一行这说明整个体系是输入是手写的输出是机器生成的测试维护者只负责声明要检查哪些产物而不必手工抄写计划文本。测试数据的组织方式input 与 output 双目录测试数据以 YAML 格式组织在tests/testdata下输入tests/testdata/input每个文件是一组测试用例的列表字段包括sql必填以及可选的id、name、before、expected_outputs等输出tests/testdata/output每个文件对应同名的输入文件是运行器执行后自动生成/更新的期望结果。从输入目录的内容tests/testdata/input可以看到测试面非常广既有基础查询 basic_query.yaml、表达式与类型expr.yaml、cast.yaml、array.yaml也有各类连接与优化规则join.yaml、join_ordering.yaml、predicate_pushdown.yaml、column_pruning.yaml还有 DDL 与流式语义create_source.yaml、sink.yaml、mv_on_mv.yaml、emit_on_window_close.yaml、match_recognize.yaml以及 TPCH 基准查询 tpch.yaml。测试运行器tests/planner_test_runner.rs会遍历tests/testdata/input下所有.yml与.yaml文件以文件名去掉扩展名作为测试用例名注册到libtest-mimic框架中逐个执行并对输出目录中有输出但无对应输入的孤儿文件做清理。编写测试用例SELECT 作为用例在sql字段中直接写一条SELECT查询并通过expected_outputs声明想要校验的计划类型即可。expected_outputs支持logical_plan、stream_plan、binder_error等取值。用 binder_error 校验非法 SQLREADME 给出的第一个示例用于验证绑定器对非法 SQL 的行为- sql: | select * from t expected_outputs: - binder_error由于表t不存在Binder 阶段就会抛错。对照输出文件tests/testdata/output/basic_query.yaml可以看到期望结果是- sql: select * from t binder_error: | Catalog error Caused by: table or source not found: t这验证了绑定错误消息的稳定性——错误文案也是测试断言的一部分任何影响报错信息的改动都会被测试捕获。用 logical_plan / batch_plan 校验合法查询若 SQL 合法运行器会生成相应计划并与期望结果比对。README 的第二个示例- sql: | create table t (v1 bigint, v2 double precision); select * from t; expected_outputs: - logical_plan - batch_plancreate table语句会被当作前置环境执行执行 DDL 而非产出计划随后select * from t生成逻辑计划与批量计划。实际输出tests/testdata/output/basic_query.yaml为- sql: | create table t (v1 bigint, v2 double precision); select * from t; batch_plan: |- BatchExchange { order: [], dist: Single } └─BatchScan { table: t, columns: [t.v1, t.v2], distribution: SomeShard } stream_plan: |- StreamMaterialize { columns: [v1, v2, t._row_id(hidden)], stream_key: [t._row_id], pk_columns: [t._row_id], pk_conflict: NoCheck } └─StreamTableScan { table: t, columns: [t.v1, t.v2, t._row_id], stream_scan_type: SnapshotBackfill, stream_key: [t._row_id], pk: [_row_id], dist: UpstreamHashShard(t._row_id) }从计划树可以读出很多实现细节批量计划顶层是BatchExchange负责把多分片结果汇聚为Single分布流式计划则是StreamMaterialize包裹StreamTableScan扫描方式为SnapshotBackfill隐藏列_row_id被自动加入作为流式主键。多语句与前置 DDL一个测试用例的sql字段可以包含多条语句换行分隔DDL 在前、查询在后是常见写法运行器会顺序执行这些语句src/lib.rs。测试用例中同一条 SQL 内只允许一个查询语句——如果出现两条查询会直接panic!(two queries in one test case)。更丰富的计划类型README 仅列举了部分取值实际TestType枚举src/lib.rs支持更多检查项每个取值对应前端规划链路的一个具体阶段expected_outputs 取值对应的计划/结果底层生成调用logical_plan原始逻辑计划planner.plan(bound)后输出optimized_logical_plan_for_batch面向批量执行优化后的逻辑计划.gen_optimized_logical_plan_for_batch()optimized_logical_plan_for_stream面向流式执行优化后的逻辑计划.gen_optimized_logical_plan_for_stream()batch_plan分布式批量物理计划.gen_batch_plan().gen_batch_distributed_plan()batch_plan_proto批量计划的 Proto JSON 表示batch_plan.to_batch_prost_identity(false)batch_local_plan单机本地执行的批量计划.gen_batch_local_plan()batch_distributed_plan分布式批量计划.gen_batch_distributed_plan()stream_plan创建物化视图MV的流式计划.gen_create_mv_plan()stream_dist_plan流式计划分片后的 fragment 计划build_graphexplain_stream_grapheowc_stream_planEOWC 语义下的流式计划.gen_create_mv_plan(.., EmitMode::OnWindowClose)eowc_stream_dist_planEOWC 语义下的流式分片计划同上 build_graphbackfill_order_planBackfill 顺序计划DOT 格式explain_backfill_order_in_dot_formatsink_planSink 计划假设 blackhole sink.gen_sink_plan()binder_error/planner_error/optimizer_error各阶段报错信息对应阶段捕获的错误batch_error/batch_local_error批量计划生成阶段的错误对应阶段捕获的错误stream_error/eowc_stream_error流式计划生成阶段的错误对应阶段捕获的错误explain_outputEXPLAIN语句的输出handle_explain其中expected_outputs是一个集合HashSetTestType可同时声明多个运行器只会生成被声明的那几项src/lib.rs 中每个输出字段在生成前都会先检查expected_outputs是否包含对应TestType。可选的用例字段除sql与expected_outputs外测试用例还支持定义见 src/lib.rsid用例标识可被其他用例的before引用name用例的简短描述方便在测试报告中定位例如 basic_query.yaml 中的test boolean expression common factor extractionbefore在执行本用例 SQL 之前先执行的一组前置用例 id其语句会被展开执行create_source/create_table_with_connector通过CreateConnector结构声明以 connector 创建 source/表需提供format、encode、name与file文件内容或路径底层会构造 Kafka connector 的CREATE SOURCE/TABLE语句并把 protobuf 定义写入临时文件src/lib.rswith_config_map以键值对形式注入前端会话配置运行前通过session.set_config应用。编写测试用例EXPLAIN 作为用例当想要测试EXPLAIN CREATE ...或EXPLAIN (options) ...这类语句时可以把EXPLAIN语句整体放进sql字段此时输出字段固定为单一的explain_output- sql: explain select 1; expected_outputs: - explain_output运行器对EXPLAIN走的是explain::handle_explain处理器src/lib.rs成功时把 explain 响应文本填入explain_output失败时填入planner_error。真实用例见 explain.yaml- sql: explain (distsql, trace, verbose) select 1; expected_outputs: - explain_output - sql: | create table t1(v1 int); create table t2(v2 int); explain (logical) select * from t1 join t2 on v1v2; expected_outputs: - explain_output - sql: | explain (logical) create table t1(v1 int); expected_outputs: - explain_output从这些用例可以看到EXPLAIN测试特别适合覆盖三类场景带选项的 EXPLAINexplain (logical)、explain (distsql, trace, verbose)等EXPLAIN DDLexplain (logical) create table ...EXPLAIN 包住带 connector 的建表语句explain create table ... with (connector kafka, ...) FORMAT PLAIN ENCODE JSON——即使该外部系统并不存在规划阶段也能完成并输出 explain 结果。这使EXPLAIN用例成为验证规划器输出文本格式与错误路径部分输出的高性价比手段例如explain trace在失败时也应输出部分结果见 explain.yaml 中带 id 的用例。测试执行链路从 YAML 到计划树测试运行器对每个用例的执行逻辑src/lib.rs大致如下用Parser::parse_sql解析 SQL 为语句列表对每条语句按类型分派到不同处理路径对查询类语句Query、Insert、Delete、Update构造OptimizerContextverbose: true并调用apply_queryapply_querysrc/lib.rs内部串联完整规划链路用Binder::new_for_batch(session)绑定失败则记录binder_error用Planner::new_for_stream规划出逻辑计划失败则记录planner_error按需调用.gen_optimized_logical_plan_for_batch()/.gen_optimized_logical_plan_for_stream()失败记录optimizer_error按需生成批量计划本地/分布式与batch_plan_proto失败记录batch_error按需以EmitMode::Immediately或EmitMode::OnWindowClose生成流式计划及 fragment 图失败记录stream_error/eowc_stream_error按需生成sink_plan默认使用 blackhole connector、append-only 类型失败记录sink_errorcheck_resultsrc/lib.rs核对声明要检查的输出与实际产生的输出是否一致包括未声明却产生了输出、声明了却没有输出两种异常。计划树文本由plan.explain_to_string()生成也就是 EXPLAIN 命令展示的树状文本。更新输出自动应用新结果当修改了规划器/优化器代码或新增了测试用例后输出文件可以自动重新生成。运行./risedev do-apply-planner-test该任务在 planner_test.toml 中定义实际执行的是UPDATE_EXPECT1 cargo nextest run -p risingwave_planner_test --retries 0——通过UPDATE_EXPECT环境变量让expect-test库直接以当前实际输出覆盖期望文件任务结束后还会清理输出目录中那些没有对应输入文件的孤儿输出。README 特别提醒alias./risedev dapt等价于do-apply-planner-test。因此在本地快速刷新所有计划快照时也可以直接敲./risedev dapt运行单个测试精确回归运行单个或多个测试文件使用./risedev run-planner-test yaml file name ./risedev run-planner-test tpch # Run tpch.yaml ./risedev run-planner-test # Run all tests其底层同样是cargo nextest run -p risingwave_planner_test --retries 0planner_test.toml文件名参数直接透传给nextest作为过滤条件。注意这里的文件名对应输入目录下的 YAML 文件如tpch对应tests/testdata/input/tpch.yaml且要求本地已安装nextest任务依赖install-nextest。由于依赖nextest与UPDATE_EXPECT机制务必在开发机本地执行这些命令而不是在只读的仓库副本上运行执行后生成的 output 改动需作为代码变更的一部分提交。从源码看测试覆盖策略以 TPCH 与优化规则为例输入目录中大量 YAML 文件即是最直观的覆盖清单。以 TPCH 为例tpch.yaml 中的用例通常形如- sql: | create table t (v1 bigint, v2 double precision); select ... from t; expected_outputs: - batch_plan - stream_plan配合输出目录中的对应文件就形成了一组SQL → 批量/流式计划快照的回归基线。优化器相关目录如 predicate_pushdown.yaml、column_pruning.yaml、join_ordering.yaml、case_when_optimization.yaml则专门验证各类规则改写后的计划形态从 basic_query.yaml 中也能看到大量针对布尔表达式化简、公因子提取、常量折叠的用例如constant folding for IS TRUE, IS FALSE, IS NULL其输出正是化简后的逻辑计划。这套输入声明 输出快照的体系有几个显著优点改动即对比修改优化规则或规划器后任何计划形态变化都会在 diff 中直观暴露开发者可以据此判断改动是否符合预期错误信息也纳入回归binder_error、planner_error等字段把报错文案纳入断言避免不经意的错误信息回退一键刷新 人工审阅do-apply-planner-test可批量重写输出但重写后仍需人工检查 diff防止测试被自动化地错误固化。常见问题与注意事项输出文件是自动生成的永远不要手工编辑tests/testdata/output下的文件正确做法是修改input后运行./risedev do-apply-planner-test并人工 review 生成的 diff一个用例只放一个查询sql中多条 DDL 可以但多条查询语句会触发运行器panicbefore引用的是用例id需要复用前置环境时用before: [some_id]而不是重复贴 SQLconnector 类用例必须提供file无论是create_source还是create_table_with_connector源码src/lib.rs要求必须带file字段否则直接panic运行前提需要nextestrisedev任务会自动安装且应在可写目录的开发环境中运行不匹配即失败expected_outputs声明了但执行没产出对应结果或没声明却产出了都会被视为测试失败check_result 的实现逻辑。延伸阅读模块主文档src/frontend/planner_test/README.md测试运行器src/frontend/planner_test/tests/planner_test_runner.rs测试核心实现src/frontend/planner_test/src/lib.rs任务定义do-apply-planner-test/run-planner-testsrc/frontend/planner_test/planner_test.toml输入样例tests/testdata/input/basic_query.yaml 与 tests/testdata/input/explain.yaml输出样例tests/testdata/output/basic_query.yaml赞分享数据库流处理后端数据工程【免费下载链接】risingwaveEvent streaming platform for agentic AI. Continuously ingest, transform, and serve event streams in real time, at scale.项目地址https://gitcode.com/gh_mirrors/ri/risingwave点击查看免费下载相关推荐WSA安卓子系统完整安装教程三步装好WSABuilds内置谷歌商店与RootWSA安卓子系统完整安装教程三步装好WSABuilds内置谷歌商店与Root WSABuilds 是一个免费的开源项目让你在 Windows 10 / 1开发工具Matter (connectedhomeip) CHIP Test Suites 详解从 YAML 测试定义到多控制器测试生成Matter connectedhomeip CHIP Test Suites 详解从 YAML 测试定义到多控制器测试生成 导读 本文以 connected物联网智能家居嵌入式通信微信QQ防撤回补丁教程4步装好补丁让对方撤回无效微信QQ防撤回补丁教程4步装好补丁让对方撤回无效 晚上十一点四十客户在群里发来发票金额没错按这个走几秒后这条消息变成一行灰字。第二天早上你追问时桌面应用即时通讯上一篇如何3天搭建企业级AI交互系统Element-Plus-X全方案解析下一篇轻松掌握百度网盘API高效使用指南与实战技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门