一、概述

当整个解析过程访问到 RegularQuerySpecification 节点时,执行逻辑可以看作两部分: 首先访问 FromClauseContext 子树, 生成名为 from 的 LogicalPlan; 接下来,调用 withQuerySpecification 方法在 from 的基础上完成后续扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
override def visitRegularQuerySpecification(
ctx: RegularQuerySpecificationContext): LogicalPlan = withOrigin(ctx) {
val from = OneRowRelation().optional(ctx.fromClause) {
visitFromClause(ctx.fromClause)
}
withSelectQuerySpecification(
ctx,
ctx.selectClause,
ctx.lateralView,
ctx.whereClause,
ctx.aggregationClause,
ctx.havingClause,
ctx.windowClause,
from
)
}

二、处理 FROM 子句

$visitFromClause$ 函数是所有 from 语法的主要执行函数。

1
2
3
val from = OneRowRelation().optional(ctx.fromClause) {
visitFromClause(ctx.fromClause)
}

$visitFromClause$ 函数访问 FromClauseContext 并递归访问,一直到匹配 TableNameContext 节点(visitTableName)时,直接根据 TableNameContext 中的数据信息生成 UnresolvedRelation,此时不再继续递归访问子节点,构造名为 from 的 LogicalPlan 并返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
override def visitFromClause(ctx: FromClauseContext): LogicalPlan = withOrigin(ctx) {
val from = ctx.relation.asScala.foldLeft(null: LogicalPlan) { (left, relation) =>
val right = plan(relation.relationPrimary)
val join = right.optionalMap(left)(Join(_, _, Inner, None, JoinHint.NONE))
// 是对 relation 语法规则内部的 JOIN 语句进行处理,由于本示例sql不包含join语法此函数不做过多介绍
withJoinRelations(join, relation)
}
if (ctx.pivotClause() != null) {
if (!ctx.lateralView.isEmpty) {
throw new ParseException("LATERAL cannot be used together with PIVOT in FROM clause", ctx)
}
withPivot(ctx.pivotClause, from)
} else {
ctx.lateralView.asScala.foldLeft(from)(withGenerate)
}
}

三、添加常规 SELECT 规范

向逻辑计划添加常规(SELECT)查询规范。 查询规范是逻辑计划的核心,就是寻源(FROM子句)、投影(SELECT)、聚合(GROUP BY…HAVING…)过滤 (WHERE)。$withSelectQuerySpecification$ 函数,将 selectClause、whereClause 和刚才得到的 from 节点 等可能包含的节点传入

3.1. 处理 SELECT 子句

select 子句语法定义:

1
2
3
selectClause
: SELECT (hints+=hint)* setQuantifier? namedExpressionSeq
;

SELECT 子句的处理结果就是一个命名表达式(NamedExpression)的集合

1
SELECT record.* from (SELECT struct(a,b,c) as record ...

列剪裁逻辑对应 SQL 中 select 语句对 name 列的选择 操作, AstBuilder 在访问过程中会获取 QuerySpecificationContext 节 点所包含的 NamedExpressionSeqContext 成员,并对其所有子节点对应的表达式进行转换,生成 NameExpression 列表 namedExpressions 然后,基于 namedExpressions 生成 Project LogicalPlan;

1
2
3
4
5
6
override def visitNamedExpressionSeq(
ctx: NamedExpressionSeqContext): Seq[Expression] = {
Option(ctx).toSeq
.flatMap(_.namedExpression.asScala)
.map(typedVisit[Expression])
}

3.2. lateralView

3.3. 处理 WHERE 子句

在 QuerySpecificationContext 中包含了名称为 where 的 BooleanExpressionContext 类型,对应图 5.8 中的 BooleanDefaultContext 节点 。 AstBuilder 会对该子树进行递归访问(例如碰到 ComparisonContext 节点 时会生成 Greaterτhan 表达式),生成 expression 并返回作为过滤条件,然后基于此过滤条件表 达式生成 Filter LogicalPlan 节点 。

3.4. aggregationClause

3.5. havingClause

3.6. windowClause

3.7. visitCommonSelectQueryClausePlan