简介
Apache Calcite是一个动态数据管理框架。它包含了许多组成典型数据管理系统的经典模块,但省略了一些关键性的功能: 数据存储,数据处理算法和元数据存储库。
Calcite有意地远离了存储和处理数据的任务。如我们所见,这使得它成为在应用程序和一个或多个数据存储位置和数据处理引擎之间的最佳中间层选择。它同样也是构建数据库的完美基础选择: 只需要在它的基础上添加数据。
概念
1、关系代数(Relational algebra):关系代数是Calcite的核心。每个查询都被表示为一颗关联操作树。你可以将SQL翻译成关系代数,或者直接建立关联操作树。
2、模式适配器(schema adapter):模式适配器允许Calcite读取特定类型的数据,将数据呈现到对应的表中。
3、流(Streaming):Calcite扩展了SQL和关系代数,以支持流查询。
架构
处理流程
一般来说Calcite解析SQL有以下几步:
1、Parser. 此步中Calcite通过Java CC将SQL解析成未经校验的AST(抽象语法树),在 Calcite 中用 SqlNode 来表示。
2、Validate. 该步骤主要作用是校证Parser步骤中的AST是否合法,如验证SQL scheme、字段、函数等是否存在; SQL语句是否合法等,此步完成之后就生成了RelNode树。
3、Optimize. 该步骤主要的作用优化RelNode树, 并将其转化成物理执行计划。主要涉及SQL规则优化如:基于规则优化(RBO)及基于代价(CBO)优化; Optimze 这一步原则上来说是可选的, 通过Validate后的RelNode树已经可以直接转化物理执行计划,但现代的SQL解析器基本上都包括有这一步,目的是优化SQL执行计划。此步得到的结果为物理执行计划。
4、Execute,即执行阶段。此阶段主要做的是:将物理执行计划转化成可在特定的平台执行的程序。如Hive与Flink都在在此阶段将物理执行计划CodeGen生成相应的可执行代码。
功能
可以支持以下的功能:
1、查询解析,校验,优化
2、支持从json文件中创建模型
3、支持大多数标准函数和聚合函数
4、针对Linq4j和JDBC后端的JDBC查询
5、Linq4j front-end
6、支持SQL功能:SELECT,FROM(包括JOIN语法),WHERE,GROUP BY(包括GROUPING SETS),聚合函数(包括COUNT(DISTINCT…)和FILTER),HAVING,ORDER BY(包括NULLS FIRST / LAST),设置操作( UNION,INTERSECT,MINUS),子查询(包括相关的子查询),窗口聚合,LIMIT(语法为Postgres)。
7、支持本地或者远程JDBC drivers
8、支持多个适配器
例子
#TestCalcite.java
public class TestCalcite {
public static class HrSchema {
public final Employee[] emps = {
new Employee(100, 1),
new Employee(200, 2),
new Employee(300, 1),
new Employee(301, 3),
new Employee(305, 1)};
public final Department[] depts = {
new Department(1),
new Department(2),
new Department(3)};
}
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("org.apache.calcite.jdbc.Driver");
Properties info = new Properties();
info.setProperty("lex", "JAVA");
Connection connection = DriverManager.getConnection("jdbc:calcite:", info);
CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class);
SchemaPlus rootSchema = calciteConnection.getRootSchema();
Schema schema = new ReflectiveSchema(new HrSchema());
rootSchema.add("hr", schema);
Statement statement = calciteConnection.createStatement();
ResultSet resultSet = statement.executeQuery("select d.deptno, e.empid" +
" from hr.emps as e join hr.depts as d on e.deptno = d.deptno");
while(resultSet.next()) {
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
System.out.print(resultSet.getMetaData().getColumnName(i) + ":" + resultSet.getObject(i));
System.out.print("|");
}
System.out.println();
}
resultSet.close();
statement.close();
connection.close();
}
}
#Employee .java
public class Employee {
public int empid;
public int deptno;
public Employee(int empid, int deptno) {
this.empid = empid;
this.deptno = deptno;
}
public int getEmpid() {
return empid;
}
public void setEmpid(int empid) {
this.empid = empid;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
}
#Department .java
public class Department {
public int deptno;
public Department(int deptno) {
this.deptno = deptno;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
}