Spark-源码学习-SparkSQL-架构设计-SQL 引擎-Parser 模块
一、概述
SparkSQL 的 Parser 模块负责将 SQL 解析为 LogicalPlan,把整个 SparkSQL Parser 模块分为 构建抽象语法树
和 遍历抽象语法树
两个部分去学习😊~~~
二、Parser 模块架构设计
2.1. 架构设计
2.1.1. ParseInterface
Catalyst 中提供了直接面向用户的 ParseInterface 接口,该接口中包含了对 SQL 语句、Expression 表达式和 TableIdentifier 数据表标识符的解析方法。AbstractSqlParser 是实现了 ParseInterface 的虚类,其中定义了返回 AstBuilder 的函数。
2.1.2. AbstractSqlParser
SparkSqlParser
SparkSQL 的 SparkSqlParser,将 SQL 解析为 LogicalPlan。
CatalystSqlParser
CatalystSqlParser 用于 Catalyst 内部,而 SparkSqlParser 用于外部调用
2.1.3. SqlBaseBaseVisitor
Spark 在 AstBuilder 中重写了 SqlBaseBaseVisitor 的一些访问方法,这个类主要适用于 Catalyst 内部调用,而外部调用则由 AstBuilder 的子类 SparkSqlAstBuilder 负责。
不论是 SparkSqlParser 还是 CatalystSqlParser,都是对访问者 SqlBaseVisitor (SparkSqlParser 中是SparkSqlAstBuilder,CatalystSqlParser中是AstBuilder) 的一个封装而已,底层干活的都是它~
三、实现
3.1. 构建抽象语法树
Spark 提供了一个 .g4 文件,编译的时候会使用 Antlr 根据这个 .g4 生成对应的词法分析类和语法分析类,同时还使用了访问者模式,用以构建语法树。
3.2. 遍历语法树
3.2.1. Visitor
Spark SQL 编译器中主要采用 Visitor 方式,在遍历过程中,对每个元素都实施 $accept()$ 方法,在每个元素的 $accept()$ 方法中回调访问者的 $visit$ 方法,从而使访问者针对对象结构设计不同的具体访问者类来完成不同的操作。实现 Visitor 中的关键逻辑,直接调用 ANTLR4 生成的各个模块来访问语法分析器解析得到的语法树,最后返回结果。
AstBuilder
AstBuilder,继承了 ANTLR4 生成的默认 SqlBaseBaseVisitor,用于生成 SQL 对应的抽象语法树 AST
$visitSingleStatement$
语法树中 SingleStatementContext 是根节点,但是在访问该节点时一般什么都不做,只递归访问子节点。
1
2
3override def visitSingleStatement(ctx: SingleStatementContext): LogicalPlan = withOrigin(ctx) {
visit(ctx.statement).asInstanceOf[LogicalPlan]
}ctx.statement
调用 $getRuleContext$ 方法查找 SingleStatementContext 的子节点中第一个类型是 StatementContext 的节点
1
2
3public StatementContext statement() {
return getRuleContext(StatementContext.class,0);
}visit
开始遍历获取的
StatementContext
节点1
2
3public T visit(ParseTree tree) {
return tree.accept(this);
}$ctx.statement$ 返回
StatementContext
,StatementContext
并没有实现 $accept$ 方法。调用其父类的父类
RuleContext
的 $accept()$ 方法1
2
3public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
return visitor.visitChildren(this);
}接着 $RuleContext.accept()$ 调用 $AstBuilder.visitChildren()$:
1
2
3
4
5
6
7override def visitChildren(node: RuleNode): AnyRef = {
if (node.getChildCount == 1) {
node.getChild(0).accept(this)
} else {
null
}
}返回 QueryContext,并调用其 $accept()$ 方法~
$visitQuery$
在 query 语句下,$StatementContext—>RuleContext.accept()$ 调用 $AstBuilder.visitChildren()$ 返回 QueryContext,并继续调用其 $accept()$ 方法~
Query
语法定义:1
2
3query
: ctes? queryTerm queryOrganization
;visitQuery:
1
2
3
4override def visitQuery(ctx: QueryContext): LogicalPlan = withOrigin(ctx) {
val query = plan(ctx.queryTerm).optionalMap(ctx.queryOrganization)(withQueryResultClauses)
query.optionalMap(ctx.ctes)(withCTE)
}引用本站文章Spark-源码学习-SparkSQL-架构设计-编译器体系-AstBuilder-QueryContextJoker
SparkSqlAstBuilder
SparkSqlAstBuilder 继承 AstBuilder,并在其基础上定义了一些 DDL 语句的访问操作,主要在 SparkSqlParser 中调用。
3.2.2. Listener
PostProcessor
UnclosedCommentProcessor
四、总结
当面临开发新的语法支持时,首先需要改动的是 ANTLR4 文件(在 SqlBase.g4 中添加文法), 重新生成词法分析器(SqlBaseLexer)、语法分析器( SqlBaseParser)和访问者类( SqlBase Visitor 接口与 SqlBaseBaseVisitor 类),然后在 AstBuilder 等类中添加相应的访问逻辑,最后添加执行逻辑。