注意:本文基于 Calcite 1.35.0 版本源码进行学习研究,其他版本可能会存在实现逻辑差异,对源码感兴趣的读者请注意版本选择

前言

在上一篇 Apache Calcite System Catalog 实现探究中,我们介绍了经典的数据库的处理流程,包括:SQL 解析SQL 绑定SQL 优化以及计划执行。SQL 绑定主要的作用是将 SQL 解析生成的 AST 和数据库的元数据进行绑定,从而生成具有语义的 AST。SQL 绑定会通过自底向上的方式遍历 AST,对抽象语法树中的节点进行绑定分析,绑定的过程中会将表、列等元数据附在语法树上,最后生成具有语义的语法树 Bounded AST

Calcite 通过 SQL 校验器实现 SQL 绑定,SQL 校验器所需的 System Catalog 信息,我们在上篇文章已经做了详细的介绍,感兴趣的读者可以阅读回顾相关内容。本文将重点介绍 Calcite SQL 校验器的整体设计,梳理校验器中不同类的用途,然后通过一些案例来展示 SQL 校验器的整体流程,并对流程中的关键方法进行代码级别的分析,力求让大家能够深刻理解 Calcite 的 SQL 校验器。

SQL 校验器整体设计

SQL 校验器的核心类为 SqlValidator,它负责使用 Calcite 元数据信息对 AST 进行验证,最终生成具有语义信息的 AST。在 Calcite 中,可以通过 SqlValidatorUtil.newValidator 方法快速创建一个 SqlValidator。

除了 SqlValidator 校验器类之外,Calcite 为了将 SQL 中的名称解析为对象,还在校验器内部构建了两个对象:SqlValidatorScopeSqlValidatorNamespace,SqlValidatorScope 表示名称解析的范围,代表了在查询中的某一个位置,当前可见的字段名和表名。SqlValidatorNamespace 则表示了校验过程中查询语句的数据源,不同的查询位置都有不同类型的 namespace 类,例如:表名对应的 IdentifierNamespace,Select 语句对应的 SelectNamespace,以及 UNIONEXCEPTINTERSECT 对应的 SetopNamespace。下面我们针对核心的 SqlValidator、SqlValidatorScope 和 SqlValidatorNamespace 分别进行探究,了解其设计细节以及适用场景。

SqlValidator

SqlValidator 校验器根据元数据对 SQL 解析的 AST 进行校验,得到具有语义信息的绑定 AST。SqlValidator 通过访问者模式对 AST 进行校验,调用 SqlNode#validate 方法时,校验器内部会调用 validateXxx 方法,例如:调用 SqlLiteral.validate(SqlValidator, SqlValidatorScope) 会调用 validateLiteral(SqlLiteral); ,调用 SqlCall.validate(SqlValidator, SqlValidatorScope) 则会调用 validCall(SqlCall, SqlValidatorScope);

SqlValidator 接口定义了 Calcite 校验器的主要方法,它提供了基础的 getCatalogReadergetOperatorTable 方法,分别用于获取元数据信息和运算符、函数。校验 SqlNode 则是通过 validate 方法,会按照 AST 结构进行遍历校验,最终返回已校验 SqlNode。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface SqlValidator {

// 获取校验器使用的 CatalogReader,用于获取元数据信息
SqlValidatorCatalogReader getCatalogReader();

// 获取校验器使用的 SqlOperatorTable,用于获取运算符和函数
SqlOperatorTable getOperatorTable();

// 校验 SqlNode 对应的表达式树,返回已校验的树
SqlNode validate(SqlNode topNode);

// 获取已验证节点的类型
RelDataType getValidatedNodeType(SqlNode node);

// 获取 SqlNode 所属的 Namespace
SqlValidatorNamespace getNamespace(SqlNode node);

// 展开 * 号对应的列
SqlNodeList expandStar(SqlNodeList selectList, SqlSelect query, boolean includeSystemVars);

// 展开 order by 子句中的序号和别名列
SqlNode expandOrderExpr(SqlSelect select, SqlNode orderExpr);

// 返回 SqlNode 结果集列的原始类型,该类型中包含 catalog, schema, table, column
List<@Nullable List<String>> getFieldOrigins(SqlNode sqlQuery);
}

此外,为了对 SqlValidator 校验过程中的一些行为进行控制,Calcite 提供了 SqlValidator#Config 配置类,通过 with 方法可以方便地设置校验器的属性,常见的属性设置方法如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Config {

// 默认 SqlValidator 配置类
SqlValidator.Config DEFAULT = ImmutableSqlValidator.Config.builder().withTypeCoercionFactory(TypeCoercions::createTypeCoercion).build();

// 配置默认 NULL 值排序规则
Config withDefaultNullCollation(NullCollation nullCollation);

// 配置列引用是否展开
Config withColumnReferenceExpansion(boolean expand);

// 配置 SQL 兼容模式
Config withConformance(SqlConformance conformance);
}

SqlValidatorScope

TODO

SqlValidatorNamespace

TODO

SQL 校验器执行流程

TODO

结语

TODO

写在最后

笔者因为工作原因接触到 Calcite,前期学习过程中,深感 Calcite 学习资料之匮乏,因此创建了 Calcite 从入门到精通知识星球,希望能够将学习过程中的资料和经验沉淀下来,为更多想要学习 Calcite 的朋友提供一些帮助。

Calcite 从入门到精通