Calcite sql2rel 过程

发布时间 2023-06-17 17:46:49作者: Aitozi

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]])

转化的入口
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]])

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的结构