4. SQL解析基础:词法分析、语法分析、抽象语法树(AST)的概念与作用

在构建数据血缘分析系统时,SQL解析是第一步,也是最关键的一步。只有将原始的SQL文本转化为计算机可理解的结构化表示,才能进一步追踪字段的来龙去脉。本章将深入解析SQL解析的三个核心阶段:词法分析、语法分析与抽象语法树(AST),并阐明它们在血缘追踪中的实际作用。

4.1 为什么需要SQL解析?

SQL本质上是一种字符串。对于数据库引擎而言,它需要理解字符串的含义才能执行;对于血缘分析工具而言,它需要理解字符串的结构才能提取字段间的依赖关系。直接使用正则表达式匹配SQL中的表名和字段名,在面对子查询、CTE、JOIN、函数嵌套等复杂场景时,极易出错且难以维护。

SQL解析通过将文本转换为结构化的树形数据,使得程序能够精确地访问SQL中的每一个元素(表、字段、别名、函数、条件等),从而为血缘分析提供可靠的底层数据。

核心观点: 没有精确的SQL解析,就没有可靠的数据血缘。正则表达式只能处理“平面”文本,而AST能处理“立体”的嵌套结构。

4.2 解析三阶段概览

SQL解析通常分为三个连续的阶段,每个阶段都有其特定的输入、输出和任务。下表总结了这三个阶段的核心差异:

阶段 输入 输出 核心任务 血缘分析中的价值
词法分析 原始SQL字符串 Token流(标记序列) 将字符序列拆分为有意义的单词(Token) 识别关键字(SELECT, FROM)、标识符(表名、字段名)、操作符
语法分析 Token流 抽象语法树(AST) 根据语法规则,将Token流组织成树形结构 建立SQL的层级关系(如:SELECT -> 字段列表 -> 表达式)
语义分析 AST 带语义信息的AST 验证类型、作用域、引用合法性 解析表别名、字段所属表、函数返回值类型
注意: 在数据血缘分析中,我们通常关注前两个阶段(词法分析和语法分析)生成的AST。语义分析虽然重要,但往往在AST生成后,由血缘分析引擎结合元数据(如数据字典)来完成。

4.3 词法分析:从字符串到Token流

词法分析器(Lexer或Tokenizer)是解析过程的第一道工序。它像是一个“单词切割机”,将输入的SQL字符串按照语言规范,切割成一个个不可再分的Token(词法单元)。

每个Token通常包含两个部分:类型(如:关键字、标识符、数字、字符串、操作符)和值(原始文本)。

例如,对于以下SQL语句:

SELECT a.name, b.salary FROM employee AS a JOIN department AS b ON a.dept_id = b.id;

词法分析器会将其拆分为如下Token序列(简化表示):

Token类型 Token值
关键字SELECT
标识符a
操作符.
标识符name
分隔符,
标识符b
操作符.
标识符salary
关键字FROM
标识符employee
关键字AS
标识符a
关键字JOIN
标识符department
关键字AS
标识符b
关键字ON
标识符a
操作符.
标识符dept_id
操作符=
标识符b
操作符.
标识符id
分隔符;
常见误区: 词法分析阶段不关心SQL语法是否正确。例如,SELECTT a FROM b 中的 SELECTT 会被识别为一个标识符(而非关键字),词法分析器不会报错。语法错误将在下一阶段被捕获。

4.4 语法分析与抽象语法树(AST)

语法分析器(Parser)接收词法分析器产生的Token流,并根据SQL的语法规则(通常由上下文无关文法定义),将这些线性排列的Token组织成一个树状结构——抽象语法树(AST)。

AST是SQL的“骨架”,它去除了无关紧要的语法细节(如分号、括号的优先级等),只保留程序逻辑的核心结构。每个节点代表一个语法结构(如:SelectStatement、JoinClause、ColumnReference、FunctionCall)。

以下是对应上述SQL语句的简化AST结构示意图:

SQL抽象语法树(AST)结构示例 SelectStatement SelectClause FromClause JoinClause ColumnRef ColumnRef a name b salary TableSource (employee AS a) TableSource (department AS b) OnCondition Equals a.dept_id b.id AST去除了分号、括号等语法细节,保留了SELECT、FROM、JOIN、ON等核心逻辑结构。 每个节点代表一个语法成分,子节点代表其组成部分。

4.5 词法分析与语法分析在血缘追踪中的作用

理解这两个阶段,对于编写高质量的血缘解析代码至关重要:

  • 词法分析阶段: 负责识别出所有的“标识符”(Identifier)。在血缘分析中,我们需要从Token流中初步筛选出所有可能是表名或字段名的Token。例如,SELECT a.name 中的 a 和 name 都会被标记为标识符,但此时还不知道 a 是别名还是表名。
  • 语法分析阶段: 负责建立“上下文关系”。AST告诉我们 a.name 是一个 ColumnRef(字段引用),并且它位于 SelectClause 下。更重要的是,AST将 FROM employee AS a 中的 a 与 employee 关联起来,从而在后续分析中,我们可以通过遍历AST,将 a.name 解析为 employee.name。
实战要点: 在编写血缘分析工具时,我们通常不直接操作Token流,而是遍历AST。因为AST已经包含了SQL的完整结构信息。例如,要找出所有被SELECT的字段,只需遍历AST中所有类型为 ColumnRef 的节点即可。

4.6 解析器生成工具与血缘分析实践

在实际项目中,我们很少手动编写词法分析器和语法分析器。通常使用解析器生成工具(Parser Generator)或现成的SQL解析库。以下是几种常见的选择:

工具/库 语言 特点 在血缘分析中的适用性
ANTLR Java, Python, JS等 强大的语法定义能力,支持多种目标语言,社区有现成的SQL语法文件 非常适合构建跨数据库类型的血缘分析工具
sqlparse Python 轻量级,纯Python实现,解析速度快 适合快速原型开发或对解析精度要求不高的场景
Calcite Java Apache顶级项目,提供完整的SQL解析、验证和优化框架 企业级数据平台(如Hive, Flink)内部广泛使用
JSqlParser Java 专门用于解析SQL的Java库,API友好 Java技术栈中实现血缘分析的常用选择

下图展示了使用解析器工具进行血缘分析的一般流程:

基于AST的数据血缘分析流程 SQL文本 原始字符串 SQL解析器 词法+语法分析 抽象语法树 结构化表示 血缘分析 遍历AST 提取依赖 血缘分析引擎的核心工作 1. 遍历AST,查找所有 ColumnRef 节点,提取字段名。 2. 查找 TableSource 节点,建立表名与别名的映射。 3. 解析 FunctionCall 节点,识别聚合或计算函数对字段的影响。 4. 结合元数据(数据字典),输出字段级血缘关系(如:employee.name -> result.name)。

4.7 本章小结

  • 词法分析 将SQL字符串拆解为Token流,是解析的基础。
  • 语法分析 将Token流组织成抽象语法树(AST),是解析的核心产出。
  • AST是数据血缘分析的“地图”,它清晰地展示了SQL语句中各个元素的层级和引用关系。
  • 在实际工程中,应优先使用成熟的解析库(如ANTLR、JSqlParser、Calcite)来生成AST,而非手动解析。
  • 血缘分析的本质,就是高效、准确地遍历AST,并结合元数据信息,推导出字段级别的数据流向。