二、实现

2.1. $parsePlan$

调用 $sqlParser.parsePlan$ 执行 parse,SparkSqlParser 类中没有实现 $parsePlan$ 函数,调用父类 $AbstractSqlParser.parsePlan$:

1
2
3
4
5
6
7
8
override def parsePlan(sqlText: String): LogicalPlan = parse(sqlText) { parser =>
astBuilder.visitSingleStatement(parser.singleStatement()) match {
case plan: LogicalPlan => plan
case _ =>
val position = Origin(None, None)
throw QueryParsingErrors.sqlStatementUnsupportedError(sqlText, position)
}
}

$parsePlan$ 函数调用了 $parse$ 函数,并且代入一个函数参数: SqlBaseParser => T

1
protected def parse[T](command: String)(toResult: SqlBaseParser => T): T = {...}

$parse$ 中核心逻辑是:

$toResult$ 函数即:

2.1. 构造语法树

Spark 使用 ANTLR (Another Tool for Language Recognition) 进行词法和语法解析,生成语法树。

1
val ctx = parser.singleStatement()

当面临开发新的语法支持时,首先需要改动的是 ANTLR 4 文件(在 SqlBase.g4 中添加文法),重新生成词法解析器(SqlBaseLexer)、语法解析器(SqlBaseParser)和访问者接口 (SqlBaseVisitor) 与访问者类(SqlBaseBaseVisitor),然后在 AstBuilder 等类中添加相应的访问逻辑,最后添加执行逻辑。

2.2. 遍历语法树

1
2
3
4
5
6
7
8
withOrigin(ctx, Some(sqlText)) {
astBuilder.visitSingleStatement(ctx) match {
case plan: LogicalPlan => plan
case _ =>
val position = Origin(None, None)
throw QueryParsingErrors.sqlStatementUnsupportedError(sqlText, position)
}
}

2.2.1. $ParserUtils#withOrigin$

CurrentOriginTreeNode 提供了一个位置,以便询问其来源的上下文。例如,当前正在分析哪一行代码。

2.2.2. $AstBuilder#visitSingleStatement$

Spark SQL 首先会在 ParserDriver 中通过调用语法分析器中的 $singleStatement$ 方法构建整棵语法树,然后通过 AstBuilder 访问者类对语法树进行访问。 根据 AstBuilder 中的逻辑,其访问入口即是 $visitSingleStatement$ 方法,该方法也是访问整棵抽象语法树的启动接口。