深度解析JQuick-SQL AST到逻辑计划转换机制|分布式SQL原理
Meta 描述:本文围绕jquick-sql架构原理说明JQuickASTToLogicalPlanVisitor如何把强类型AST转化为逻辑计划,梳理Table Scan、Filter、Project、Join与GroupBy的组装顺序,连接Java分布式SQL引擎架构、SQL查询优化原理和后续gRPC分布式数据交换。
标签:jquick-sql架构原理、Logical Plan、AST Visitor、Table Scan、SQL查询优化原理、Java分布式SQL引擎架构
官网示例:
- FORMULAS 示例页:https://www.jquick.org/jquick-sql
- github 地址:https://github.com/paohaijiao/jquick-sql
前言
SQL 被解析为 JQuickQueryNode 后,JQuick-SQL 还不会立刻选择 Hash Join 或 Exchange。它先生成逻辑计划(Logical Plan):用数据扫描、过滤、投影、连接和聚合等算子描述查询意图,而不绑定本地或分布式执行细节。负责这一步的是 JQuickASTToLogicalPlanVisitor。
逻辑计划是优化器的重要输入。只有先把 SQL 结构表达成节点树,Predicate Pushdown、Projection Pushdown、Join Reorder 等规则才有可重写的对象边界。
Visitor入口与SELECT主干
转换入口非常直接:visit(JQuickQueryNode node) 取得查询中的 SelectStatement,再交给对应的 visit 方法。针对常规 SELECT,源码的组装顺序清晰体现了 SQL 结构的依赖关系:
text
FROM → JOIN → WHERE → GROUP BY/HAVING → SELECT → ORDER BY → LIMIT
首先访问 FROM,生成根计划;如果存在多个 JQuickJoinClauseNode,就逐个将 Join 接到当前根上。WHERE 存在时,Visitor 将条件转换为 JQuickExpression,再包装为 JQuickFilterNode。这个顺序让过滤位于投影之前,也为后续将条件下推到 Scan 或 Join 附近保留空间。
从AST子句到逻辑节点
普通表会生成 JQuickTableScanNode;子查询则递归调用 Visitor,得到独立逻辑子树。SELECT 没有 GROUP BY 且不包含聚合函数时,Visitor 创建 JQuickProjectNode。带 GROUP BY 时会构造 JQuickGroupByNode,保留分组键、聚合项与 HAVING 条件,并重建与聚合结果匹配的投影。
如果 SELECT 中出现聚合函数但没有 GROUP BY,源码会构造空 group keys 的全局聚合计划。这与 SQL 的全表聚合语义一致:例如 COUNT(*) 不需要分组列,但仍需要 Aggregate 节点。
排序与行数控制在主干尾部实现:ORDER BY 对应 JQuickSortNode,LIMIT/OFFSET 由 visitLimit 构造 JQuickLimitNode。因此 Logical Plan 还没有 TopN、具体并行度或数据交换策略,那些属于物理阶段。
CTE、集合操作与递归结构
JQuick-SQL 的 Visitor 不只处理单表 SELECT。源码还覆盖 CTE、递归 CTE、集合操作、Values 与空结果等结构。集合语义由 JQuickSetOperationNode 表示;WITH 结构对应 JQuickWithNode;递归场景使用 JQuickRecursiveUnionNode。这些节点让复杂 SQL 在逻辑层保持结构,而不是过早内联为难以优化的表达式。
这种表示方式也说明逻辑计划不等于关系代数教科书中的最小集合:它还承担保存项目特有 SQL 结构的责任,直到后续生成器决定对应的物理节点。
实操代码案例
以下 SQL 可作为观察转换链路的输入,语法特征均来自项目 README 的嵌入式查询示例:
java
JQuickDataSet result = sql.execute(
"SELECT id, name, age FROM users " +
"WHERE age >= 25 ORDER BY age DESC"
);
在计划层,可将其理解为:JQuickTableScanNode(users) 先形成来源,JQuickFilterNode(age >= 25) 表示过滤,再由 JQuickProjectNode 保留 id/name/age,最后接上 JQuickSortNode。这是逻辑解释,不意味着上述类是该 facade 直接暴露的调用接口。
常见问题
逻辑计划是否已经决定执行算法?
没有。它描述查询算子和关系,Hash Join、Nested Loop Join、Hash Aggregate、Exchange 等在物理计划阶段确定。
HAVING为什么不作为普通WHERE处理?
WHERE 过滤原始行;HAVING 依赖分组或聚合结果。Visitor 会在 GroupBy 分支中保留 HAVING 条件。
子查询会丢失边界吗?
不会。Visitor 对子查询递归建立逻辑子树,再由相关节点保存其结构与引用关系。
总结与拓展
JQuickASTToLogicalPlanVisitor 将强类型 AST 变为可优化的逻辑节点树。它以 FROM 为起点,按 JOIN、WHERE、聚合、投影、排序和限制组织节点,并覆盖 CTE、集合与递归结构。理解这层,是掌握 SQL查询优化原理和后续物理规划的关键。
Core Advantage
Logical nodes preserve SQL meaning without locking in an execution algorithm.
Applicable Scenarios
Plan inspection, rule-based optimization, subquery planning, and SQL feature extension.