1.结构基础
在整个SQL解析过程中,在优化阶段,SQL的表示形式是逻辑节点树,这个阶段有两个核心的类,RelNode和RexNode,都是Calcite中的定义
RelNode,关系表达式,表现为TableScan、Project、Sort、Join等,是节点树的核心
RexNode,注释说明为行表达式,表现为RexLiteral(常量)、RexCall(函数)、RexInputRef(输入引用)等
2.RelNode/AbstractRelNode
RelNode是上层的基本接口,规定了一些方法;AbstractRelNode是RelNode的实现,后续的具体操作的类型都是基于AbstractRelNode的子类。
AbstractRelNode的几个成员如下,其中rowType只有get方法没有set方式,构造函数里也没有设置的地方,在子类里设置,应该主要在Project这个实现中。
/**
* Cached type of this relational expression.
*/
protected RelDataType rowType;
/**
* The digest that uniquely identifies the node.
*/
@API(since = "1.24", status = API.Status.INTERNAL)
protected RelDigest digest;
private final RelOptCluster cluster;
/** Unique id of this object, for debugging. */
protected final int id;
/** RelTraitSet that describes the traits of this RelNode. */
protected RelTraitSet traitSet;
- RelDataType
类型信息,代表一行或一个表达式结果的数据类型信息。 - RelDigest
作为唯一标识使用,内容基本等同于relNode本身 - RelOptCluster
优化查询期间相关关系表达式的环境,汇集了Planner, RexBuilder, MetadataQuery 等各种信息,在SqlNode 转换 RelNode 前创建,后面一直充当类似上下文的作用 - RelTraitSet
在Calcite中没有使用不同的对象代表逻辑和物理算子,但是使用trait来表示一个算子的物理属性
RelTrait的有序集合,RelTrait表示特征定义中关系表达特征的表现,表示RelNode的物理属性,用来定义逻辑表的物理相关属性
RelTraitDef代表RelTrait的类型,有三种:ConventionTraitDef、RelCollationTraitDef、RelDistributionTraitDef,RelTrait的实现类基本上也就是这三个类型
ConventionTraitDef代表转换,RelCollationTraitDef代表排序,RelDistributionTraitDef代表分布
3.LogicalJoin
LogicalJoin的类继承关系如下,可以看到,上层还有两个父类:Join和BiRel
3.1.BiRel
BiRel是所有两个input形式的关系表达式的一个基类,相对AbstractRelNode来说,多了两个成员:左右输入。
protected RelNode left;
protected RelNode right;
UML图如下
3.2.Join
Join形式的一个基类,主要添加了如下几个成员
protected final RexNode condition;
protected final ImmutableSet<CorrelationId> variablesSet;
protected final ImmutableList<RelHint> hints;
/**
* Values must be of enumeration {@link JoinRelType}, except that
* {@link JoinRelType#RIGHT} is disallowed.
*/
protected final JoinRelType joinType;
protected final JoinInfo joinInfo;
variablesSet和hints是附加的参数和提示,普通状态下都为空,暂不关注。
condition是条件表达式,RexNode类型
JoinInfo,记录join操作的信息,三个成员:leftKeys、rightKeys、nonEquiConditions。分别记录该join使用到的左右子树列集合和非相等条件
joinType,join的类型,有INNER、LEFT、RIGHT、FULL、SEMI、ANTI
SEMI类型对应的形态如下
* SELECT * FROM EMP
* WHERE EXISTS (SELECT 1 FROM DEPT
* WHERE DEPT.DEPTNO = EMP.DEPTNO)
ANTI是SEMI的相反
SELECT * FROM EMP
* WHERE NOT EXISTS (SELECT 1 FROM DEPT
* WHERE DEPT.DEPTNO = EMP.DEPTNO)
3.3.样例
以如下sql语句为样例,查看LogicalJoin几个关键的成员的信息
SELECT * FROM t1 join t2 ON t1.id = t2.id AND t1.id < 1000
condition的结果如下,代表的就是SQL的条件语句:
joinType和JoinInfo信息如下
rowType的信息如下,代表的就是左右子树的类信息
4.LogicalSort
LogicalSort的类继承关系如下,可以看到,上层还有两个父类:Sort和SingleRel
4.1.SingleRel
是所有一个input形式的关系表达式的一个基类,增加的成员就只有一个input
protected RelNode input;
UML图如下
4.2.Sort
排序相关的一个基类,增加的成员如下
public final RelCollation collation;
public final RexNode offset;
public final RexNode fetch;
fetch是返回的数据量,使用limit语句时对应的limit的数量
offset是返回前需要丢弃的数量,大部分为空,可以在这个上做优化,降低返回数据量
collation是对排序的描述,最终类为RelFieldCollation,包含两个关键枚举成员:Direction、NullDirection
Direction就是排序方式:ASCENDING(“ASC”)、STRICTLY_ASCENDING(“SASC”)、DESCENDING(“DESC”)、STRICTLY_DESCENDING(“SDESC”)、CLUSTERED(“CLU”)
SASC、SDESC与前一个的差别就是包不包含相等的值,ASC是正向排序,后一个可以等于前一个;SASC后一个大于前一个
CLU没有特性顺序,使用Hash排序一般会出现这种排序方式
NullDirection代表null的排序,应该就是null放在什么位置,因为null值不能比较:FIRST、LAST、UNSPECIFIED。UNSPECIFIED表示未指定
4.3.样例
以如下sql语句为样例,查看LogicalSort几个关键的成员的信息
SELECT id FROM t1 ORDER BY id LIMIT 3
collation结果如下
fetch和offset结果如下
5.LogicalFilter
LogicalFilter继承自Filter,再往上是就是SingleRel
5.1.Filter
过滤器,有一个成员
protected final RexNode condition;
condition就是过滤条件
5.2.LogicalFilter
增加了一个成员
private final ImmutableSet<CorrelationId> variablesSet;
5.3.样例
以如下sql语句为样例,查看LogicalFilter几个关键的成员的信息
SELECT id FROM t1 where t1.id < 1000
condition结果如下
input结果如下
6.LogicalTableScan
LogicalTableScan继承自TableScan,直接继承自AbstractRelNode
6.1.TableScan
成员如下
protected final RelOptTable table;
protected final ImmutableList<RelHint> hints;
主要是table,表示表的信息
6.2.RelOptTableImpl
RelOptTable的实现之一,主要成员如下
private final RelOptSchema schema;
private final RelDataType rowType;
private final Table table;
private final Function<Class, Expression> expressionFunction;
private final ImmutableList<String> names;
/** Estimate for the row count, or null.
*
* <p>If not null, overrides the estimate from the actual table.
*
* <p>Useful when a table that contains a materialized query result is being
* used to replace a query expression that wildly underestimates the row
* count. Now the materialized table can tell the same lie. */
private final Double rowCount;
6.3.TableSourceTable
Flink对RelOptTable的实现,目前Flink的解析最后都是走到TableSourceTable上的,扩展自Calcite的Prepare.AbstractPreparingTable
结构如下
class TableSourceTable(
relOptSchema: RelOptSchema,
rowType: RelDataType,
statistic: FlinkStatistic,
val tableSource: DynamicTableSource,
val isStreamingMode: Boolean,
val contextResolvedTable: ContextResolvedTable,
val flinkContext: FlinkContext,
val abilitySpecs: Array[SourceAbilitySpec] = Array.empty)
extends FlinkPreparingTableBase(
relOptSchema代表一组关系数据集,Flink实现了CalciteCatalogReader接口,用于读取表元数据使用
rowType数据各列的类型信息
statistic表统计信息
tableSource数据表类,可以转换为Calcite表
isStreamingMode是否流模式
contextResolvedTable解析表,解析该表源来自哪里。有三种表类型:永久表、临时表、匿名表
flinkContext上下文信息,包含一些相关配置之类的
6.4.样例
以如下sql语句为样例,查看LogicalFilter几个关键的成员的信息
SELECT id FROM t1 where t1.id < 1000
首先看整体结构,在TableScan上面封装了一层Project
table使用的类是TableSourceTable,内容如下
relOptSchema结构如下
statistic结构如下
tableSource的结构如下
contextResolvedTable结构如下
flinkContext结构如下
7.LogicalProject
继承自Project,上层是SingleRel,核心就是在Project里的一个成员
protected final ImmutableList<RexNode> exps;
protected final ImmutableList<RelHint> hints;
exps对应的应该就是其input所返回的列信息。继续以如下sql语句为样例,查看LogicalProject的关键信息
SELECT id FROM t1 where t1.id < 1000
在两个不同的节点上的LogicalProject,可以看到其exps的信息不一样
首先input是LogicalTableScan的,其exps有三列,就是LogicalTableScan的返回
之后input是LogicalFilter的,因为只返回一列,所以exps就是一列