一、概述

QueryContext 可以视为 query 语句的根结点~

二、源码

1
2
3
4
5
override def visitQuery(ctx: QueryContext): LogicalPlan = withOrigin(ctx) {
val query = plan(ctx.queryTerm).optionalMap(ctx.queryOrganization)(withQueryResultClauses)
// Apply CTEs
query.optionalMap(ctx.ctes)(withCTE)
}

2.1. Query

1
val query = plan(ctx.queryTerm).optionalMap(ctx.queryOrganization)(withQueryResultClauses)

2.1.1. ctx.queryTerm

在 QueryContext 的子节点中查找第一个是 QueryTermContext 类型的节点。AstBuilder 的 $plan$ 方法继续调用 QueryTermContext 的 $accept$ 方法,这个方法调用的还是 $AstBuilder.visitChildren$ 方法,

1
2
3
public QueryTermContext queryTerm() {
return getRuleContext(QueryTermContext.class, 0);
}

2.1.2. ctx.queryOrganization

一般来讲,QueryOrganizationContext 为根节点所代表的子树中包含了各种对数据组织的操作,例如 Sort、Limit 和 Window 算子等

在 QueryContext 的子节点中查找第一个是 QueryOrganizationContext 类型的节点。

1
2
3
public QueryOrganizationContext queryOrganization() {
return getRuleContext(QueryOrganizationContext.class,0);
}

2.1.3. withQueryResultClauses()

添加 QueryOrganizationContext 子句到逻辑计划中。

2.2. Apply CTEs

1
query.optionalMap(ctx.ctes)(withCTE)

2.2.1. ctx.ctes

在 QueryContext 的子节点中查找第一个是 CtesContext 类型的节点。

2.2.2. withCTE

添加 CtesContext 子句到逻辑计划中。

1
2
3
4
5
6
7
8
9
10
11
12
private def withCTE(ctx: CtesContext, plan: LogicalPlan): LogicalPlan = {
val ctes = ctx.namedQuery.asScala.map { nCtx =>
val namedQuery = visitNamedQuery(nCtx)
(namedQuery.alias, namedQuery)
}
val duplicates = ctes.groupBy(_._1).filter(_._2.size > 1).keys
if (duplicates.nonEmpty) {
throw QueryParsingErrors.duplicateCteDefinitionNamesError(
duplicates.mkString("'", "', '", "'"), ctx)
}
UnresolvedWith(plan, ctes.toSeq)
}

三、总结

AstBuilder.visitQuery 方法就是结合 QueryContext 的 3 个子节点 QueryTermContext,QueryOrganizationContext,CtesContext 的信息进行包括 ORDER BY/SORT BY/CLUSTER BY/DISTRIBUTE BY/LIMIT/WINDOWS 子句 和 CTE 的处理,其中 QueryTermContext 是信息的主要承载方,默认是其子类 QueryTermDefaultContext 来实例化。