首页 / 资讯中心 / 文章详情

Presto 查询引擎内核详解:SQL Analyzer 语义分析机制——从 AST 到 Analysis

Presto 查询引擎内核详解:SQL Analyzer 语义分析机制——从 AST 到 Analysis ★ FEATURED ARTICLE
1. 概述在 Presto 查询引擎中一条 SQL 语句从文本形式到最终可执行的物理计划需要经历一系列处理阶段其中Parser 阶段只负责将 SQL 文本转换为抽象语法树AST它描述的是 SQL 的语法结构但并不包含任何语义层面的理解。以一条简单的查询为例SELECT name FROM customerParser 只能得到Identifier(“name”)然而这个标识符背后所蕴含的语义信息Parser 完全无从知晓name 属于哪个表或子查询该字段的数据类型是什么VARCHAR、INT 还是其他customer 对应的是哪张物理表其元数据信息如何获取后续执行时应当使用哪个函数或数据源对象。这些关键的语义信息正是 Analyzer 阶段需要填充的空白。Analyzer 的核心产物是 Analysis 对象它承载了 SQL 从语法结构转换为语义模型过程中所产生的全部解析信息。2. AnalysisAnalyzer 的语义分析结果Analysis 是 Presto Analyzer 阶段最终交付的核心成果。它并非某种单一维度的信息而是一个各种类型及各种层次的语义分析结果的聚合容器维护着 AST 节点与各类语义对象之间的映射关系。从逻辑层面来看Analysis 可以视作如下的结构Analysis | -- Expression - Type -- Expression - ResolvedField -- Table - TableHandle -- FunctionCall - FunctionHandle -- Node - Scope -- ...其中Type记录每个表达式经过类型推导后得到的数据类型ResolvedField记录标识符经过语义解析后最终指向的具体字段TableHandle记录 SQL 中引用的表名对应的具体数据源句柄FunctionHandle描述函数调用对应的具体函数实现句柄Scope为具有查询语义边界的 AST 节点如 QuerySpecification、Relation、Subquery 等提供字段可见性的描述即该节点所能感知的字段集合和关系类型。等等…因此Analysis 是在 AST 的语法骨架之上构建的一层语义完形它充当着连接 SQL AST 世界与查询执行计划世界的语义映射桥梁。3. ScopeSQL 语义作用域模型3.1 Scope 的定义Scope 是 Presto Analysis 中用于描述 SQL 语义作用域semantic scope的核心结构。从本质上看Scope 表示某个 AST 节点经过语义分析后形成的语义输出模型描述该节点对外暴露的 RelationType、Field 以及其他命名语义信息例如 Named Query这些信息被其他分析过程用于 Identifier 解析和语义绑定。其中最核心的信息是RelationType relationType它描述该 AST 节点作为一个 SQL Relation 时对外可见的字段集合。例如对于查询语句SELECT name FROM customer;假设customer 表包含 (id, name, age) 三个字段。经过 Analyzer 分析之后customer 表所对应的 Scope 信息为Table (customer) Scope RelationType: --id int --name varchar --age int这表示customer 这个 Relation 对外暴露可见的字段为 id, name, age。而整个 QueryBlock 所对应的 Scope 信息为QuerySpecification Scope RelationType: --name varchar这表示该 QuerySpecification 对外暴露可见的字段只有 name。因此Scope 描述的是一个 SQL 语义节点在关系模型中的对外语义输出信息。这些信息同时也是后续语义分析的重要输入。3.2 Scope 在 Analysis 中的组织方式Scope 并不是独立存在的全局结构而是 Analysis 中维护的一类语义信息。源码逻辑上MapNode, Scope scopes;表示Scope 是映射到特定 AST Node 上的语义分析结果。例如上述查询对应的 AST 语法树为Query | -- QuerySpecification | -- Select -- Where | -- Table(customer)经过语义分析之后对应的 Scope 映射信息为Query -- Scope(Query) QuerySpecification -- Scope(QuerySpecification) Table(customer) -- Scope(Table)因此Scope 在 Analysis 中本质上是一种 Node - Scope 的平铺映射关系而不是一棵与 AST Tree 完全对应的 Scope Tree。这是因为并不是每一个 AST 节点都需要维护自己专门的 Scope。只有那些能够形成独立 Relation 输出或者引入新的 SQL 语义边界的节点才需要 Scope典型的包括QueryQuerySpecificationRelationSubquery 等等反之Projection、Filter、Join 条件中的表达式节点等等都不会专门创建 Scope它们只是在所属 Scope 中完成字段解析和类型推导。3.3 Scope 的 Parent 关系与外部语义访问虽然 Scope 在 Analysis 中整体表现为Node - Scope的平铺映射关系但是 Scope 本身却包含了OptionalScope parent用于描述特殊情况下的外部语义访问关系。需要强调的是Scope.parent 并不是 AST 父节点对应的 Scope而是当前语义节点可以访问的外部语义环境。一个典型场景是 Correlated Subquery。例如对于如下的语句SELECT * FROM orders o WHERE EXISTS ( SELECT 1 FROM customer c WHERE c.id o.customer_id );对于内部 Query 来说其可以通过 customer 表对应的 Scope 来解析 c.idTable (customer) Scope RelationType: --id int --name varchar --...但是对于 o.customer_id 来说由于来自外层 Query所以必须将来自外层的包含了表 orders 信息的 Scope 传递进来才能完成语义解析Outer Scope RelationType: --orders.customer_id int --orders.customer_name varchar --...在实际执行字段解析时查找方向为Current Scope | v Parent Scope | v 继续向上查找直到找到匹配字段。这种关系更多来自 SQL 语义依赖而不是 AST 父子层级关系。另一个典型场景是 lateral join其右子树需要接收左子树对应的 Scope 作为其 Parent才能完成语义解析。3.4 Scope 的核心作用总结综合来看Presto 中的 Scope 可以理解为AST Tree -- AST Node | | Analyzer v Analysis -- Scope | -- RelationType | | | v | 对外暴露字段 | -- Named Query | -- Parent Scope | v 外部语义访问链它承担两个核心职责 描述 AST 节点分析后的语义输出诸如Relation 输出字段QueryBlock 输出字段Named Query 信息。 通过 Parent Scope 支持跨 QueryBlock 的语义引用例如Correlated SubqueryCTE-based queryLateral Join。因此Scope 并不是 AST 的镜像结构也不是简单的变量查找表而是 Presto Analyzer 为 SQL 语义模型建立的 Relation 输出接口和名称解析基础设施为 Identifier Resolution 提供字段绑定依据。4. Analyzer 的语义分析模型Presto Analyzer 本质上是一个基于 AST Visitor 的语义分析过程。它的核心任务是在 Parser 生成的 AST 基础上结合数据库元数据和 SQL 语义规则建立完整的语义模型并将分析结果保存到 Analysis 中。整体过程可以理解为Analyzer 并不是简单地遍历 AST 节点并记录信息而是在递归分析过程中根据当前节点的语义规则驱动其依赖的子节点进行分析在必要情况下向子节点传递外部语义环境例如 Parent Scope基于子节点返回的分析结果完成当前节点的语义推导生成当前节点对应的语义结果并保存到 Analysis 中。因此从属性计算角度看Analyzer 同时具有自顶向下的语义环境传递自底向上的语义结果计算。4.1 自顶向下的语义环境传递在某些 SQL 语义场景中外层语义环境需要传递给内部查询以完成语义解析。典型包括Correlated SubqueryCTENamed QueryLateral Join 等。以 3.3 小节所列举的 Correlated Subquery 为例外层语义环境的传递如下图所示Outer Query (orders) │ │ Scope (orders) 作为 Parent Scope 向下传递 ▼ Inner Subquery (customer) │ │ 字段解析时先在 customer Scope 中查找 │ 若未找到沿 Parent 链向上查找 orders Scope ▼ o.customer_id → 解析为 orders.customer_id细节请参见 3.3 小节此处不再赘述。再次强调一下Scope.parent 表示的是 SQL 语义上的可见范围而不是 AST 父节点关系。4.2 自底向上的语义计算在 Analyzer 的核心计算过程中子节点提供语义信息父节点利用这些信息完成自身语义推导并进一步产生新的语义结果。例如对于查询语句SELECT c.name FROM customer c WHERE c.age 18;在分析 QuerySpecification 语法树节点时并不是简单按照 SQL 文本顺序处理各个子句而是根据不同子句之间的语义依赖关系确定分析顺序。其中FROM 子句首先决定当前 QueryBlock 所依赖的 Relation Scope后续表达式分析如 WHERE、SELECT、GROUP BY 等都依赖该 Scope 完成字段解析和类型推导。visitQuerySpecification() | -- analyzeFrom() | -- analyzeWhere() | -- analyzeSelect() | -- ...实际执行分析的步骤如下第一步分析 FROM 子句分析 FROM customer c 时Analyzer 通过 Metadata 获取 customer 表的元数据customer 表结构 - id int - name varchar - age int据此为表别名 c 生成对应的 ScopeScope(customer c) RelationType: --c.id int --c.name varchar --c.age int该 Scope 描述了 customer 这一 Relation 对外暴露的完整字段结构。第二步分析 SELECT 与 WHERE 中的表达式接着Analyzer 基于 FROM 产生的 Relation Scope对后续子句中的表达式进行字段解析和类型推导。以 c.name 为例字段解析链路如下Identifier(c.name) | v Scope(customer c) ← 从中查找匹配字段 | v ResolvedField(customer.name)ResolvedField 记录了该字段的完整绑定信息所属 Relation、表别名、字段索引及数据类型。ResolvedField 的关键价值在于消歧义它将 SQL 文本中的 c.name 绑定到 customer 表的 name 列使后续 Logical Planner 无需再次进行语义查找可以直接基于物理位置生成执行计划。与此同时表达式类型推导也在同步进行。例如c.age 18 | v BOOLEAN第三步生成当前节点的语义结果当 QuerySpecification 完成其所有子句SELECT、WHERE 等的分析后会根据自身输出构造对应的 Scope。以 SELECT c.name 为例该查询的输出仅包含一个字段Scope(QuerySpecification): RelationType: --name这个 Scope 描述的是该 QuerySpecification 作为一个 Relation 被外层引用时对外暴露的字段结构。第四步语义分析结果信息记录到 Analysis在分析过程中Analyzer 会持续向 Analysis 注册已经确定的语义信息例如表达式类型、字段绑定、聚合信息、排序信息等等。并在 Analysis 的 scopes 映射关系中添加QuerySpecification → Scope(QuerySpecification): name至此对 QuerySpecification 语法树节点的语义分析就执行完毕了位于 AST 树上的父节点可以基于这些分析结果来执行自己的语义分析了。综上所述Presto Analyzer 本质上是在 AST 之上构建 SQL 语义模型的递归分析过程通过必要的语义环境传递、基于依赖关系的递归分析以及自底向上的语义推导将 SQL 从语法结构转换为可供 Logical Planner 使用的完整语义模型。5. 总结从 Presto 内核角度看Analyzer 的本质不是生成新的语法树而是在原始 AST 之上建立一个完整的语义模型使后续 Logical Planner 可以直接从 SQL 世界进入关系执行计划世界。Presto Analyzer 语义分析体系的核心思想AST Tree | StatementAnalyzer | ------------------------- | | | Analysis information AST Tree | | Scope Mapping | | ------------------------- | Logical Planner其中Analysis 是 Analyzer 阶段生成的完整语义模型Scope 是 Analysis 中用于描述某个语义节点字段可见范围的核心结构Scope 以 Node → Scope 的形式维护并不是 AST 的镜像层级结构只有那些能够产生字段集合Fields或引入新字段可见性边界的 AST 节点才需要 Scope。普通表达式如 c.name不创建 Scope而是在所属 Scope 中完成字段和类型解析但包含子查询的表达式会间接触发子查询 Scope 的创建。Scope 的 parent 关系只用于表达外部语义环境访问例如相关子查询和 Semi Join并非 AST 父子关系。如果说 Parser 解决的是“SQL 长什么样”那么 Analyzer 解决的是“SQL 表达什么”。它将面向文本的 SQL 语言转换为面向关系代数的语义模型是 Planner / Optimizer 理解 SQL 的第一步。作者王冬PrestoDB Global Committer | Presto Iceberg Code OwnerGitHub: https://github.com/hantangwangdEmail: mingwbdgmail.com本文章同步发表于https://hantangwangd.github.io/zh/posts/2026-07-30-analyzer-from-ast-to-analysis.html
阅读完成 · 觉得有帮助?
咨询建站