上一篇已经初始化完成sparkSession,以及各种初始化的类,从这篇开始我们着重说catalyst的整体流程。
第一个流程是sql语句经过语法和词法分析解析成Unresolved Logical Plan。
SparkSession.sql()
从上一篇我们知道sqlParser=SparkSqlParser, SparkSqlParser是spark解析sql预发成LogicalPlan的核心类。 SparkSqlParser继承了抽象类AbstractSqlParser。
最终调用的是AbstractSqlParser.parsePlan()
astBuilder的初始化是在SparkSqlParser中,继承关系如下
astBuilder = SparkSqlAstBuilder -> AstBuilder-> SqlBaseBaseVisitor,
在这个地方spark使用了Antlr4进行语法和词法的解析。
SqlBase.g4
SqlBase.g4文件是Antlr4的基础文件,
路径:sql/catalyst/src/main/antlr4/org/apache/spark/sql/catalyst/parser/SqlBase.g4
下图就是一些常用的查询语法,想要深入的了解源码中的执行内容,就需要对SqlBase.g4文件有一定的了解,这个对后续自定义一些语法有很大的帮助。
比如我们常用的:
- relation 一般指查询的hive表,文件等
- namedExpression 变量
- valueExpression 变量的值
下面就是我用idea的插件运行了一个sql.
SqlBase.g4在编译执行之后会创建一些执行文件,spark会继承这些文件做自定义的操作。比如AstBuilder 继承SqlBaseBaseVisitor.
以SqlBaseBaseVisitor为例,其实就是以SqlBase.g4中按照语法来生成的,spark只需继承重写部分函数即可。
SparkSqlParser
回到ParseDriver的parsePlan(), 我们可以知道,astBuilder = SparkSqlParser
下图中通过解析整个语法树,sql转换成逻辑执行计划,当前生成的逻辑执行计划还是Unresolved Logical Plan。
Tree
Tree是Catalyst执行计划表示的数据结构。LogicalPlans,Expressions和Pysical Operators都可以使用Tree来表示。Tree具备一些Scala Collection的操作能力和树遍历能力。
Tree有三种
- UnaryNode:一元节点,即只有一个子节点 ag: Project
- BinaryNode:二元节点,即有左右子节点的二叉节点 ag : Join
- LeafNode:叶子节点,没有子节点的节点 ag: HiveTableRelation
Tree有两个子类继承体系,即QueryPlan和Expression
QueryPlan下面的两个子类分别是LogicalPlan(逻辑执行计划)和SparkPlan(物理执行计划)
Expression是表达式体系,是指不需要执行引擎计算,而可以直接计算或处理的节点
以AstBuilder.visitQuerySpecification为例
这个地方就可以理解SparkSqlParser 是怎么把Tree的节点转换成LogicalPlan.
我以一个sql为例来看一下它具体的执行计划
select count(org_id), bu_cd from dim_org group by bu_cd limit 1024