前言
calcite是一个能够将任意数据查询转换成基于sql查询的引擎,引擎特性也有不少,好比支持sql树的解析,udf的扩展,sql执行优化器的扩展等等。目前已经被不少顶级apache项目引用,好比hive,kylin等。在这个SQL做为主流的数据查询语言大数据世界里,calcite的做用会愈来愈大。html
理解calcite的核心流程
如图1所示。calcite核心步骤有两个,数据关系化, SQL解析执行。java
图1 calcite核心流程node
数据协议关系化
要想使用sql进行查询,首先要把非结构化数据结构化,而calcite号称支持全部数据协议,则必然得将这部份内容抽象出来。git
在calcite的接口中,Schema和Table是数据关系化中最重要的两个接口。Schema是对catalog或者是database的抽象,以兼容已经存在的各种数据库,Table是对表,视图,流的抽象,以兼容数据的各类场景,下面详细描述一下这两种抽象。github
schema
calcite利用schema的层级关系,构造出来namespace的概念,如图2所示,schema自身是一个树形结构,这样设计的优势很明显,能够兼容全部已知和未知的数据库,基于namespace结构,schema不管是横向仍是纵向均可以无限扩展。sql
图2 schema的namespace数据库
在实际应用中,RootSchema是根全部schema的路径,全部注册在RootSchema上的table或者是udf都是全局的,意思就是说能够被SubSchema直接使用,而注册在SubSchema里的table或者是udf,则在使用中必须声明是哪一个SubSchema拥有的。apache
table
table是schema的核心属性,一个schema拥有多个table,这就像一个数据库中有不少表同样。而table的概念更为普遍,为了兼容到各种数据库或者消息队列,calcite将table类型细分为TableType,基本的相似传统关系型数据库中的表或者视图,流式的Stream等。json
另外对数据协议的兼容是很是重要的,像json,csv,xml等等,table抽象出了RelDataType接口,目的是将应用层的数据协议转关系化,从而能够为sql服务。数据结构
拿csv格式的数据来讲,假设csv数据的每一行数据和table中的每一行一一对应,那么在关系化的过程当中,必须将csv中每一个字段的类型及一些元数据定义清晰,好比字段是int类型仍是long类型,主键是哪一个字段,外键是哪一个字段等,calcite提供了几乎全部已存在的字段类型。
关于嵌套数据,calcite也考虑的很周全,提供MapSqlType或者ArraySqlType的形式来兼容这些结构。
拿json格式的数据来讲,字段嵌套是很常见的,若是想把这类结构数据关系化,一般有两种选择(1)数据扁平化,将嵌套的字段提上来,造成a.b的形式(2)经过calcite引擎声明嵌套字段及规则,在使用sql查询的时候再经过calcite的表达式提取这些字段。
SQL解析执行
到了这一步,其实和传统数据库很像了,如图3所示,calcite采用了该方案来解决从sql的输入到输出。
calcite经过关系代数来实现对sql的执行,而关系代数之间经过树形结构做为载体,每个输入的sql命令都会被转换成树形结构的关系代数也就是关系表达式树。calcite支持直接构建关系表达式树,经过RelBuilder接口。
注:关系代数,常见的有(交,并,差,投影,选择,笛卡尔积,链接)
图3
案例
完整案例
calcite提供了基于json和csv的案例,在calcite-example模块下,另外在该模块的单元测试中,有一些完整的例子。
sql解析案例
// 能够经过SqlParser直接对sql语句做解析,返回的就是sql树。 SqlParser sqlParser = SqlParser.create("select * from \"table\" where \"column\" > 1 limit 1"); SqlNode sqlNode = sqlParser.parseQuery(); if(sqlNode instanceof SqlCall){ if(sqlNode instanceof SqlBasicCall){ SqlBasicCall basicCall = (SqlBasicCall) sqlNode; System.out.println(((SqlIdentifier)basicCall.operand(0)).getSimple()); System.out.println(((SqlNumericLiteral)basicCall.operand(1)).getValue()); System.out.println(basicCall.getKind()); } System.out.println(sqlNode.getKind()+" -> "+sqlNode.getClass()); SqlCall call = (SqlCall) sqlNode; for(SqlNode node: call.getOperandList()){ parse(node); } }
引用
// calcite官网
http://calcite.apache.org/docs/tutorial.html
// calcite github