
ClickHouse v22.4.3.3-stable 补丁解析JOIN 与 COLUMNS 列匹配器的空指针解引用缺陷修复【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse本篇文章聚焦 ClickHouse 官方补丁版本 v22.4.3.3-stable 中的一项关键 Bug 修复在 JOIN 查询中结合COLUMNS(...)列匹配器使用时可能触发的空指针解引用nullptr dereference。文章将结合 changelog 归档文档、解析器与查询改写器源码以及 stateless 测试用例从缺陷表象、触发机制、底层实现到验证方法进行全链路拆解帮助读者理解 COLUMNS 匹配器在复杂 JOIN 场景下的行为边界并掌握如何复现与验证该修复。一、版本背景一次面向稳定版的紧急修补在 v22.4.3.3-stable 变更日志中该版本被标记为Bug Fix (user-visible misbehaviour in official stable release)即官方稳定版中用户可见的错误行为修复。其唯一一条变更记录如下Backported in #36582修复 JOIN 与 COLUMNS matcher 中的空指针解引用此修复解决了 #36416 问题对应 PR #36417由 Amos Bird 提交#36430。这里包含几个关键信息修复性质空指针解引用nullptr dereference属于会导致服务端崩溃segfault或未定义行为的严重缺陷影响范围同时涉及两条查询路径——JOIN多表连接与COLUMNS列匹配器column matcher版本关系该修复作为 backport 进入 v22.4.3.3-stable说明其同时在主开发分支对应 #36430与稳定分支对应 #36582中被合入修复作者Amos BirdClickHouse 社区核心贡献者之一。从版本命名规则看v22.4.3.3-stable 属于 v22.4 系列的第三轮补丁.3其比较基线为 v22.4.2.1-stable说明这是针对稳定分支的快速迭代修补用户升级到该版本即可规避此缺陷。二、缺陷主角COLUMNS 列匹配器是什么要理解该缺陷首先需要明确COLUMNS(...)在 ClickHouse SQL 中的地位。它不是普通函数而是一种列匹配器column matcher允许在SELECT列表中按正则表达式或显式列名批量展开列。在解析器实现中共定义了四类匹配器 AST 节点AST 节点类型SQL 形态匹配规则ASTColumnsRegexpMatcherCOLUMNS(pattern)按正则表达式匹配列名ASTColumnsListMatcherCOLUMNS(a, b, c)匹配显式列名列表ASTQualifiedColumnsRegexpMatchertable.COLUMNS(pattern)限定表名 正则匹配ASTQualifiedColumnsListMatchertable.COLUMNS(a, b)限定表名 显式列名从 ASTColumnsMatcher.cpp 的appendColumnName与formatImpl实现可以看到正则型匹配器ASTColumnsRegexpMatcher内部持有pattern字符串并且同时支持两种呈现形式// 常规形式 ostr COLUMNS ( quoteString(pattern) ); // 兼容 * LIKE 的形式format_as_asterisk_like 为真时 ostr * (asterisk_like_case_insensitive ? ILIKE : LIKE ) quoteString(asterisk_like_pattern);这说明 ClickHouse 在后续演进中还为* LIKE pattern语法提供了统一的 AST 表示而updateTreeHashImpl将pattern纳入 SipHash 计算确保不同写法但语义相同的查询在查询缓存中保持一致。在反序列化路径ASTColumnsMatcher.cpp#L287-L353中可以看到ASTColumnsListMatcher的column_list成员被强制要求只能包含标识符ASTIdentifier否则抛出BAD_ARGUMENTS异常——这是保证匹配器语义安全的关键校验。与列转换器Transformer的组合COLUMNS(...)通常与APPLY、EXCEPT、REPLACE等列转换器columns transformer联用例如SELECT COLUMNS([jk]) APPLY(toString) APPLY(length) FROM columns_transformers; SELECT COLUMNS(i, j, k) APPLY(quantiles(0.5)) FROM columns_transformers;在 01470_columns_transformers.sql 测试中详细覆盖了以下行为* APPLY(sum)对全部列应用聚合函数columns_transformers.* EXCEPT(j) APPLY(avg)限定表 排除列 应用函数EXCEPT STRICT i严格模式若列不存在则报错NO_SUCH_COLUMN_IN_TABLE多个APPLY/REPLACE串联时的语义顺序REPLACE在APPLY之后将无法再匹配到列。从源码结构看ASTColumnsMatcher系列节点均持有transformers成员类型为ASTColumnsTransformerList展开匹配的列后逐个套用转换器这解释了测试中EXCEPT在APPLY之后匹配不到任何列的语义。三、缺陷触发场景JOIN 与 COLUMNS 的碰撞空指针解引用往往发生在匹配器需要解析为具体列但解析上下文不完整的路径上。最典型的场景正是在 JOIN 查询中使用 COLUMNS 匹配器此时 ClickHouse 的查询改写器需要提前将匹配器展开为具体的列表达式列表。01839_join_to_subqueries_rewriter_columns_matcher.sql 给出了该场景的回归测试样例-- 基础场景三个表的 LEFT JOIN列名存在冲突test 列在三个表中同名 SELECT (if(a.test a, b.test, c.test)) AS a.test FROM (SELECT 1 AS id, a AS test) a LEFT JOIN (SELECT 1 AS id, b AS test) b ON b.id a.id LEFT JOIN (SELECT 1 AS id, c AS test) c ON c.id a.id; -- 缺陷场景在 JOIN 中直接使用 COLUMNS 匹配器 SELECT COLUMNS(test) FROM (SELECT 1 AS id, a AS test) a LEFT JOIN (SELECT 1 AS id, b AS test) b ON b.id a.id LEFT JOIN (SELECT 1 AS id, c AS test) c ON c.id a.id;第二个查询中COLUMNS(test)会匹配到来自 a、b、c 三个表的test列。由于多表同名列的存在JOIN 改写器必须决定每个匹配到的列归属哪张表、是否需要重命名这一解析过程对当前正在处理的表这一上下文非常敏感。若匹配器展开发生在 JOIN 左侧/右侧子查询尚未建立表引用或表引用缺失的阶段就可能在访问表的列信息指针时发生空指针解引用。需要说明的是changelog 仅记录了JOIN 与 COLUMNS matcher 中发生空指针解引用这一事实具体崩溃栈细节并未在该文档中展开上述对触发条件的描述是基于 JoinToSubqueryTransformVisitor.cpp 中匹配器展开逻辑的推断。四、源码级剖析JOIN 改写器如何展开 COLUMNS 匹配器JOIN 查询中 COLUMNS 匹配器的展开工作由 JoinToSubqueryTransformVisitor.cpp 中的extractColumnsRegexpMatcher与extractQualifiedColumnsRegexpMatcher承担。对于正则型匹配器JoinToSubqueryTransformVisitor.cpp#L161-L182其核心逻辑为const ASTColumnsRegexpMatcher columns_regexp_matcher, ... String pattern columns_regexp_matcher.getPattern(); // 编译正则 auto regexp std::make_uniqueOptimizedRegularExpression(pattern); if (!regexp) throw Exception(COLUMNS pattern {} cannot be compiled: {}, pattern, regexp.error()); // 遍历表的所有列逐列匹配正则 // ... // 对匹配到的列应用 transformer for (const auto transformer : columns_regexp_matcher.transformers-children) applyColumnsTransformer(transformer, columns);而限定表名版本JoinToSubqueryTransformVisitor.cpp#L185-L214则要求qualifier必须存在if (!qualified_columns_regexp_matcher.qualifier) throw Exception(ErrorCodes::LOGICAL_ERROR, Qualified COLUMNS matcher must have a qualifier); auto identifier qualified_columns_regexp_matcher.qualifier-asASTIdentifier ();这段代码揭示了两个潜在的空指针风险点与本缺陷高度相关transformers子节点的类型检查代码中虽遍历transformers-children但注释明确指出Qualified COLUMNS matcher must only have children of IASTColumnsTransformer type若 AST 构造路径存在偏差applyColumnsTransformer对非 transformer 节点的强制转换可能落空表列信息的解析匹配正则时依赖预先从表元数据构建的table_columns映射JoinToSubqueryTransformVisitor.cpp#L67-L81并按照当前表名 → 别名 → 原始表名的顺序查找列集合。从源码结构可以推断当 JOIN 中出现无法解析的表引用或列引用为空时该查找链上的指针访问就可能产生空指针解引用——这正是本次修复所要消除的崩溃路径。五、修复带来的防御性改进虽然 changelog 没有给出具体补丁 diff但从上述源码现状可以归纳该修复落地后形成的防御性保障严格的类型前置校验限定型匹配器强制要求qualifier存在缺失时抛出LOGICAL_ERROR而非静默解引用正则编译失败显式报错COLUMNS pattern {} cannot be compiled异常确保了非法正则不会进入匹配循环JSON 反序列化字段校验在 ASTColumnsMatcher.cpp 中pattern、qualifier、column_list缺失时分别抛出BAD_ARGUMENTS且column_list的子节点必须全部为ASTIdentifier堵住了畸形 AST 流入执行层的入口回归测试固化01839_join_to_subqueries_rewriter_columns_matcher.sql将JOIN COLUMNS组合作为 stateless 测试永久纳入测试套件防止缺陷复发。对于开发者而言该修复的启示在于凡是匹配器需要延迟解析为具体对象的语法糖COLUMNS、*、EXCEPT/REPLACE在 JOIN 多表上下文中的展开都必须建立完整的表-列引用解析链任何一环缺失都应快速失败fail fast并给出可读错误而非放任空指针崩溃。六、如何在本地验证与回归当前仓库可直接运行相关测试验证该功能路径的健康状态验证 COLUMNS 匹配器基础语义执行 01470_columns_transformers.sql它覆盖了正则匹配、显式列列表、APPLY/EXCEPT/REPLACE组合以及EXPLAIN SYNTAX输出验证 JOIN COLUMNS 组合执行 01839_join_to_subqueries_rewriter_columns_matcher.sql该文件直接针对本缺陷场景编写若测试通过则说明匹配器在 JOIN 改写路径上不再触发崩溃观察 AST 展开结果使用EXPLAIN SYNTAX SELECT COLUMNS(test) FROM ...查看匹配器展开后的实际列表达式验证列归属与重命名是否符合预期。运行 stateless 测试的标准方式为# 在已构建 clickhouse 二进制的基础上 ./tests/clickhouse-test 01470_columns_transformers 01839_join_to_subqueries_rewriter_columns_matcher七、总结v22.4.3.3-stable 作为 v22.4 系列的快速补丁版其核心价值在于消除了 JOIN 查询与 COLUMNS 列匹配器组合使用时的空指针解引用崩溃。通过 changelog 记录、AST 解析实现、JOIN 改写器展开逻辑以及两条 stateless 回归测试的相互印证可以完整还原该缺陷的成因与修复边界。对于在生产环境使用 v22.4 系列并大量依赖 COLUMNS 匹配器做动态列投影的团队升级至 v22.4.3.3-stable或之后的稳定版是规避该崩溃的推荐做法。【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考