sql2rel的过程是将SqlNode 转化成RelNode的过程

SqlToRelConverterTest中添加样例测试

  @Test void testScan() {
String sql = "SELECT * FROM EMP WHERE empno < 10";
sql(sql).ok();
}

会生成如下的relnode tree.

LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
LogicalFilter(condition=[<($0, 10)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])

Validator

registerQuery

主要实现类是在SqlValidatorImpl

org.apache.calcite.sql.validate.SqlValidatorImpl#registerQuery()



主要做的事就是遍历这个query将各个部分的SqlNode对应的SqlValidatorScope以及SqlValidatorNamespace保存起来,供后续validate和sql2rel阶段使用。

这两个概念看起来有点重复,看注解的意思应该是scope主要负责name-resolution,将一个name 解析成相应的namespace来

* Name-resolution scope. Represents any position in a parse tree than an
* expression can be, or anything in the parse tree which has columns.
*
* <p>When validating an expression, say "foo"."bar", you first use the
* {@link #resolve} method of the scope where the expression is defined to
* locate "foo". If successful, this returns a
* {@link SqlValidatorNamespace namespace} describing the type of the resulting
* object.

而namespace描述了SqlNode所对应的relation,如返回类型

 * A namespace describes the relation returned by a section of a SQL query.

在convertQuery完成后就注册了整个Query的各个SqlNode的映射关系

validate

注册完成后会执行validate阶段,这部分会涉及语义,类型等校验,比如校验select的where条件

  1. condition中没有aggregate或window aggregate
  2. condition Node 自身的校验逻辑(最终还是会路由到SqlValidator的实现上)
  3. 校验返回类型是boolean的



校验完成之后就会调用下面的接口开始进行toRel的转化

SqlToRel

转化的入口

org.apache.calcite.sql2rel.SqlToRelConverter#convertQuery

这里面最重要的就是通过 convertQueryRecursive递归的进行转化

convertSelect

SELECT `EMP`.`EMPNO`, `EMP`.`ENAME`, `EMP`.`JOB`, `EMP`.`MGR`, `EMP`.`HIREDATE`, `EMP`.`SAL`, `EMP`.`COMM`, `EMP`.`DEPTNO`, `EMP`.`SLACKER`
FROM `CATALOG`.`SALES`.`EMP` AS `EMP`
WHERE `EMP`.`EMPNO` < 10

我们这个query的主体是一个SqlSelect所以会通过convertSelect来转化

public RelNode convertSelect(SqlSelect select, boolean top) {
final SqlValidatorScope selectScope = validator().getWhereScope(select);
final Blackboard bb = createBlackboard(selectScope, null, top);
convertSelectImpl(bb, select);
return castNonNull(bb.root);
}

封装了两个对象 SqlValidatorScope 和 Blackboard。最后我们转化完成的RelNode的root会保存在blackboard中,也就是bb.root.

convertFrom

`CATALOG`.`SALES`.`EMP` AS `EMP`

convertSelect的第一步就是先转化From子句, from 子句有很多种可能, 例如以下这些

a single table ("SALES.EMP"),
an aliased table ("EMP AS E"),
a list of tables ("EMP, DEPT"),
an ANSI Join expression ("EMP JOIN DEPT ON EMP.DEPTNO = DEPT.DEPTNO"),
a VALUES clause ("VALUES ('Fred', 20)"),
a query ("(SELECT * FROM EMP WHERE GENDER = 'F')"),
or any combination of the above.

这个例子里是一个 CATALOG.SALES.EMP的 SqlIdentifer 然后就会根据这个identifier通过CatalogReader查找这个identifier所表示的table。然后会基于此创建出LogicalTableScan并将该RelNode设置到 BlackBoard中.

convertWhere

`EMP`.`EMPNO` < 10

通过Blackboard#convertExpression将上述的SqlBasicCall转化成RexCall (where条件中的是一个rex表达式)

然后会基于Blackboard的当前的root作为input创建一个LogicalFilter节点

final RelNode filter =
filterFactory.createFilter(bb.root(), convertedWhere2, ImmutableSet.of());
final RelNode r;
final CorrelationUse p = getCorrelationUse(bb, filter);
if (p != null) {
assert p.r instanceof Filter;
Filter f = (Filter) p.r;
r =
LogicalFilter.create(f.getInput(), f.getCondition(),
ImmutableSet.of(p.id));
} else {
r = filter;
} bb.setRoot(r, false);

这里会先基于前面创建的RexCall创建出filter节点,但是还会看一下这个filter中是否有 correlation variables. 如果有的话需要将这个CorrelationId 添加到Filter中, 这个样例中并没有使用。

到这里RelNode tree 已经是

LogicalFilter(condition=[<($0, 10)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])

Correlation

@Test void testCorrelationInWithSubQuery() {
String sql = "select deptno\n"
+ "from emp\n"
+ "where deptno in (select deptno\n"
+ " from dept\n"
+ " where emp.deptno = dept.deptno\n"
+ " and emp.deptno = dept.deptno)";
sql(sql).withExpand(false).withDecorrelate(false).ok();
}

上面提到在convertWhere产生Filter的时候需要处理是否有Correlation variable的场景,而这段sql就会产生这种case。correlation其实就是关联的意思,而这里where in 子句中引用了外层的emp表,所以是一个关联子查询

在WHERE的 in 的子查询中 from 只有 dept表,但是 条件引用了外层查询的emp表,所以会产生一个关联的语义。而后续可以查到Filter条件中引用了Correlation Variable的。



创建关联关系的阶段在解析SqlIdentifier时,当此identifier所对应的inputs不为空,并且identifier对应的scope不是parent scope的话namespace就意味着是对某个表的引用,而其他情况就意味着这个表可能还没有toRel,表明是from了一个correlated的item

所以创建了一个 DefferredLookup

LogicalProject(DEPTNO=[$7])
LogicalFilter(condition=[IN($7, {
LogicalProject(DEPTNO=[$0])
LogicalFilter(condition=[AND(=($cor0.DEPTNO, $0), =($cor0.DEPTNO, $0))])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
})], variablesSet=[[$cor0]])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])

convertSelectList

中间还有convert Orderby,convert Aggregate的操作,不过这个样例中没有,最后就是转化selectList,selectList会被转成Project

LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
LogicalFilter(condition=[<($0, 10)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])

这样通过不断递归convert,完成了SqlNode到RelNode的转化,中间会借助RelBuilder来创建各个RelNode 并通过BlackBoard维护当前的root节点,用于构建整个tree的结构

Calcite sql2rel 过程的更多相关文章

  1. Flink Sql 之 Calcite Volcano优化器(源码解析)

    Calcite作为大数据领域最常用的SQL解析引擎,支持Flink , hive,  kylin , druid等大型项目的sql解析 同时想要深入研究Flink sql源码的话calcite也是必备 ...

  2. [源码分析] 带你梳理 Flink SQL / Table API内部执行流程

    [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 目录 [源码分析] 带你梳理 Flink SQL / Table API内部执行流程 0x00 摘要 0x01 Apac ...

  3. 从"UDF不应有状态" 切入来剖析Flink SQL代码生成

    从"UDF不应有状态" 切入来剖析Flink SQL代码生成 目录 从"UDF不应有状态" 切入来剖析Flink SQL代码生成 0x00 摘要 0x01 概述 ...

  4. [源码分析]从"UDF不应有状态" 切入来剖析Flink SQL代码生成 (修订版)

    [源码分析]从"UDF不应有状态" 切入来剖析Flink SQL代码生成 (修订版) 目录 [源码分析]从"UDF不应有状态" 切入来剖析Flink SQL代码 ...

  5. c++ primer plus 第6版 部分二 5- 8章

    ---恢复内容开始--- c++ primer plus 第6版 部分二    5-  章 第五章 计算机除了存储外 还可以对数据进行分析.合并.重组.抽取.修改.推断.合成.以及其他操作 1.for ...

  6. calcite介绍

    前言 calcite是一个可以将任意数据查询转换成基于sql查询的引擎,引擎特性也有很多,比如支持sql树的解析,udf的扩展,sql执行优化器的扩展等等.目前已经被很多顶级apache项目引用,比如 ...

  7. Flink table&Sql中使用Calcite

    Apache Calcite是什么东东 Apache Calcite面向Hadoop新的sql引擎,它提供了标准的SQL语言.多种查询优化和连接各种数据源的能力.除此之外,Calcite还提供了OLA ...

  8. Calcite分析 - Rule

    Calcite源码分析,参考: http://matt33.com/2019/03/07/apache-calcite-process-flow/ https://matt33.com/2019/03 ...

  9. 春蔚专访--MaxCompute 与 Calcite 的技术和故事

    摘要:2019大数据技术公开课第一季<技术人生专访>,来自阿里云计算平台事业部高级开发工程师雷春蔚向大家讲述了MaxCompute 与 Calcite 的技术和故事. 具体内容包括: 1) ...

  10. 深入浅出Calcite与SQL CBO(Cost-Based Optimizer)优化

    目录 Calcite简介与CBO介绍 Calcite背景与介绍 SQL优化与CBO Calcite优化器 HepPlanner优化器与VolcanoPlanner优化器 Calcite优化样例代码介绍 ...

随机推荐

  1. #差分约束系统,Spfa,SLF优化#HDU 3666 THE MATRIX PROBLEM

    题目 多组询问,给定一个\(n*m\)的矩阵\(C\)和一个区间\([L,R]\), 问是否存在一个长度为\(n\)的序列\(A\)和一个长度为\(m\)的序列\(B\), 使得所有 \[\frac{ ...

  2. JDK9的新特性:String压缩和字符编码

    目录 简介 底层实现 总结 简介 String的底层存储是什么?相信大部分人都会说是数组.如果要是再问一句,那么是以什么数组来存储呢?相信不同的人有不同的答案. 在JDK9之前,String的底层存储 ...

  3. OpenHarmony持久化存储UI状态:PersistentStorage

      前两个小节介绍的LocalStorage和AppStorage都是运行时的内存,但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistentStor ...

  4. 拥抱开源更省钱「GitHub 热点速览」

    免费.低成本.自托管.开源替代品...这些词就是本周的热门开源项目的关键字.常见的 AI 提升图片分辨率的工具,大多是在线服务或者调用接口的客户端,而「Upscaler」是一款下载即用的免费 AI 图 ...

  5. HMS Core助力同程旅行,打造更贴心的用户出行体验

    作为中国在线旅行行业的创新者,同程旅行聚焦年轻.时尚.个性的消费群体,致力于为用户提供更便捷.聪明.安全的出行服务.近年来,同程旅行通过人工智能等创新科技的应用将平台原本的交易撮合角色转变为" ...

  6. 应用缺少POI数据,如何开发地点深度信息?

    用户在App里搜索某个地点时,并不满足单一的地点信息,希望得到更多可以帮助其做决策的深度信息.例如有打车出行需求的用户,在打车App里搜索地点时可以显示周边的地点,精确到某个路口,让用户可以自由选择合 ...

  7. Spark常见的问题以及解决方案

    Spark为什么比Hadoop要快? Spark比hadoop快的原因,我认为主要是spark的DAG机制优于hadoop太多,spark的DAG机制以及RDD的设计避免了很多落盘的操作,在窄依赖的情 ...

  8. 使用WebApi+Vue3从0到1搭建《权限管理系统》:二、搭建JWT系统鉴权

    视频地址:[WebApi+Vue3从0到1搭建<权限管理系统>系列视频:搭建JWT系统鉴权-哔哩哔哩] https://b23.tv/R6cOcDO qq群:801913255 一.在ap ...

  9. 力扣613(MySQL)-直线上的最近距离(简单)

    题目: 表 point 保存了一些点在 x 轴上的坐标,这些坐标都是整数. 写一个查询语句,找到这些点中最近两个点之间的距离. 最近距离显然是 '1' ,是点 '-1' 和 '0' 之间的距离.所以输 ...

  10. EMQX + PolarDB-X 一站式 IoT 数据解决方案

    简介: 本文整理自 EMQX 产品经理李国伟,在PolarDB开源社区中关于EMQX与PolarDB-X构建一站式IoT数据解决方案的分享.本篇内容主要分为四个部分:1. IoT数据特性 2. EMQ ...