1 | Apache Doris 源码分析3 - AST的语义分析 |
上回分析了 SQL 是如何从一个平平无奇的字符串, 变成了一个适合分析处理的对象的(AST 抽象语法树), 这次继续分析生成 AST 对象之后的经历, 故事的起点还是回到上上次的 handleQuery().
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java |
通过上次的分析, 得知在经过词法和语法分析后, 得到了一个 AST 树的列表(每个树都对应一个完整的SQL语句).接下来就是遍历每一个 AST 的对象 StatementBase, 然后为每个对象初始化一个 StmtExecutor, 之后调用executor.execute() 去执行, 下面直接进入执行的逻辑:
1 | // 位置: fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java |
上面的逻辑中分配了一个唯一标识, 然后继续:
1 | // 位置: fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java |
上述代码中标记了2个重点函数, 分别是:
- 重点函数1: 分析 AST 并产生执行计划
- 重点函数2: 调度并获得执行结果
下面依次分析这2部分内容.
分析 AST 并产生执行计划
先分析一下上面的 analyze(context.getSessionVariable().toThrift()); 函数的内部逻辑如下:
1 | // 位置: fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java |
这里继续看一下新的重点工作: 分析并生成查询计划 analyzeAndGenerateQueryPlan(tQueryOptions);函数
1 | // 位置: fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java |
上面的代码片段中, 有几个重点工作需要继续跟进分析一下:
- 重点a: 分析: 调用 parsedStmt 的 analyze 方法
- 重点b: 重写: 调用 parsedStmt 的 rewriteExprs 方法
- 重点c: 生成执行计划
下面分别跟进一下.
分析: 调用 parsedStmt 的 analyze 方法
上次源码学习的最后提到过的 analyze(Analyzer analyzer) 方法, 在这里终于被调用了, 这个方法名字叫 analyze 但是我觉得叫 prepare 也很合适, 主要包含一系列的准备工作, 下面咱们以 SelectStmt 语句的这个方法为入口, 继续跟进:
1 | // 位置: fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java |
File "<ipython-input-1-1f43be866131>", line 1
(/, 位置:, fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java)
^
SyntaxError: invalid syntax
以上就是第一步, 对抽象语法树的全部分析过程了, 可以看到, 针对每个语句能支持的语法情况, 对它内部的各个成员对象都进行了分析, 很多成员对象本身也是由内部的多个成员组成的复合型对象, 针对这种情况也会递归的调用其内部的 analyze.
以上就是语义分析的全部内容了, 想了解更多语句的语义分析细节, 可以自行到源码中去检阅.
关于词法, 语法, 语义分析的对比
这里咱们总结一下 词法分析, 语法分析, 语义分析 的区别:
- 词法分析: 把一个 sql 字符串按照规则生成一系列的单词 (或本身是一个用来吐出单词的扫描器)
- 语法分析: 把一系列的单词按照句子语法的规划生成一系列的语句
- 语义分析: 对一个句子的成员细节进行判断, 是否合理
分别举个例子会更好理解一些:
- “我 一明比 年小 岁年今”: 这句话在词法分析阶段就会返回异常, 主要原因是有些词, 比如”一明比”不是一个合法的单词.
- “小 我 明年 今年 一岁 比”: 这句话在语法分析阶段返回异常, 虽然每个单词都合法, 但是单词组合起来不是符合任何主谓宾这样的语法.
- “我 明年 比 今年 小 一岁”: 这句话可以通过词法和语法的检查, 但是在语义分析时会报异常, 主要原因是这句话的语义是不成立的.
- “我 今年 比 明年 小 一岁”: 这句话可以通过所有的检查.
如果学过编译原理, 会发现这面的这三步都是经典的编译原理的部分, 感兴趣也可以自己去学习一下.
后面的章节, 咱们会讨论语句的重写, 这一步非常关键, 之所以 sql 可以如此普及, 很大一部分的原因还是在于用户的 sql 代码可以随便写, 底层会在保证结果正确的前提之下, 会以性能更优的方式去进行重写和执行.