Apache Calcite官方文档中文版-- 概览-2. 教程

第一部分 概览

2. 教程

  本章针对Calcite的连接建立提供了循序渐进的教程,使用一个简单的适配器来将一个CSV文件目录以包含Schema信息的tables形式呈现,并提供了一个完全SQL接口。
  Calcite-example-CSV是一个Calcite中的一个功能完备的适配器,它可以读取CSV格式(以逗号分隔)的文本文件。值得称赞的是,几百行的java代码就足够提供完全的SQL查询功能。
  CSV适配器同样作为一个其他数据格式的适配器构建参考模板。尽管代码量不大,但它覆盖了一些重要的概念:
1) 用户通过使用SchemaFactory和Schema interfaces来自定义schema
2) 以JSON模型文件声明schemas
3) 以JSON模型文件声明视图views
4) 通过Table interface自定义table
5) 定义table的record类型
6) 使用Scannable Table interface作为Table的简单实现,来直接枚举所有的rows
7) 进阶实现FilterableTable,来根据简单的谓词predicates过滤rows
8) 以Translatable Table进阶实现Table,将关系型算子翻译为执行计划规则

2.1 源码搭建

版本依赖:Java (1.7 or higher; 1.8 preferred), git and maven (3.2.1 or later).

$ git clone https://github.com/apache/calcite.git
$ cd calcite
$ mvn install -DskipTests -Dcheckstyle.skip=true
$ cd example/csv

2.2 查询测试

  可以通过工程内置的sqlline脚本来连接到Calcite

$ ./sqlline
sqlline> !connect jdbc:calcite:model=target/test-classes/model.json admin admin

(如果使用Windows操作系统,命令为sqlline.bat)
执行一个metadata 查询

sqlline> !tables
+------------+--------------+-------------+---------------+----------+------+
| TABLE_CAT  | TABLE_SCHEM  | TABLE_NAME  |  TABLE_TYPE   | REMARKS  | TYPE |
+------------+--------------+-------------+---------------+----------+------+
| null       | SALES        | DEPTS       | TABLE         | null     | null |
| null       | SALES        | EMPS        | TABLE         | null     | null |
| null       | SALES        | HOBBIES     | TABLE         | null     | null |
| null       | metadata     | COLUMNS     | SYSTEM_TABLE  | null     | null |
| null       | metadata     | TABLES      | SYSTEM_TABLE  | null     | null |
+------------+--------------+-------------+---------------+----------+------+

  JDBC提示:sqlline中的 !tables 命令实际上等于执行 DatabaseMetaData.getTables() , 还有其他命令来查询JDBC metadata, 例如 !columns 和 !describe。
Apache Calcite官方文档中文版-- 概览-2. 教程
Apache Calcite官方文档中文版-- 概览-2. 教程
Apache Calcite官方文档中文版-- 概览-2. 教程
Apache Calcite官方文档中文版-- 概览-2. 教程Apache Calcite官方文档中文版-- 概览-2. 教程
  如结果所示,该系统中共有5个table: SALES schema下的EMPS, DEPTS, HOBBIES 和系统自带的 metadata schema下的COLUMNS和TABLES。系统table在Calcite中会一直展示,但其他表是由schema的指定实现而来,在本例中,EMPS和DEPTS表来源于target/test-classes路径下的EMPS.csv和DEPTS.csv文件。
  通过在这些表上执行一些查询,我们可以验证Calcite提供了完整的SQL功能实现。
首先,scan table:

sqlline> SELECT * FROM emps;
+--------+--------+---------+---------+----------------+--------+-------+---+
| EMPNO  |  NAME  | DEPTNO  | GENDER  |      CITY      | EMPID  |  AGE  | S |
+--------+--------+---------+---------+----------------+--------+-------+---+
| 100    | Fred   | 10      |         |                | 30     | 25    | t |
| 110    | Eric   | 20      | M       | San Francisco  | 3      | 80    | n |
| 110    | John   | 40      | M       | Vancouver      | 2      | null  | f |
| 120    | Wilma  | 20      | F       |                | 1      | 5     | n |
| 130    | Alice  | 40      | F       | Vancouver      | 2      | null  | f |
+--------+--------+---------+---------+----------------+--------+-------+---+

Apache Calcite官方文档中文版-- 概览-2. 教程
同时支持JOIN和GROUP BY

sqlline> SELECT d.name, COUNT(*)
. . . .> FROM emps AS e JOIN depts AS d ON e.deptno = d.deptno
. . . .> GROUP BY d.name;
+------------+---------+
|    NAME    | EXPR$1  |
+------------+---------+
| Sales      | 1       |
| Marketing  | 2       |

Apache Calcite官方文档中文版-- 概览-2. 教程
  最后,VALUES操作符可以聚合生成单独一行数据,我们可以通过这种简便的方法来测试表达式和SQL内嵌函数:

sqlline> VALUES CHAR_LENGTH('Hello, ' || 'world!');
+---------+
| EXPR$0  |
+---------+
| 13      |
+---------+

Apache Calcite官方文档中文版-- 概览-2. 教程
  Calcite具有其他许多SQL特性。我们来不及在这里一一举例,使用者可以编写更多的查询来进行验证。

2.3 Schema发现

  现在,我们来探索一下Calcite是如何发现这些table的。记住,最核心的Calcite不知道CSV文件的任何信息。(像一个“没有存储层的databse”一样,Calcite不会去了解任何文件格式)Calcite能识别这些table是因为我们告诉它去运行calcite-example-csv工程下的代码。
  运行链中有一系列的步骤。首先,我们在一个schema 工程类中以model file的格式定义一个schema。然后schema工厂类创建一个schema,schema创建多个table,这些table都知道如何通过scan CSV文件来获取数据。最后,在Calcite解析完查询并将查询计划映射到这几个table上时,Calcite会在查询执行时触发这些table去读取数据。接下来我们更深入地解析其中的细节步骤。
  在JDBC连接字符串中,我们会给出以JSON格式定义的model的路径。model具体定义如下

{
  version: '1.0',
  defaultSchema: 'SALES',
  schemas: [
    {
      name: 'SALES',
      type: 'custom',
      factory: 'org.apache.calcite.adapter.csv.CsvSchemaFactory',
      operand: {
        directory: 'target/test-classes/sales'
      }
    }
  ]
}

Apache Calcite官方文档中文版-- 概览-2. 教程
  这个model定义了一个名为SALES的schema。这个schema是由一个plugin类支持的,org.apache.calcite.adapter.csv.CsvSchemaFactory,这个类是calcite-example-csv工程里的一部分并且实现了Calcite中的SchemaFactory接口。它的create方法将一个schema实例化了,将model file中的directory参数传递过去了。

public Schema create(SchemaPlus parentSchema, String name,
    Map<String, Object> operand) {
  String directory = (String) operand.get("directory");
  String flavorName = (String) operand.get("flavor");
  CsvTable.Flavor flavor;
  if (flavorName == null) {
    flavor = CsvTable.Flavor.SCANNABLE;
  } else {
    flavor = CsvTable.Flavor.valueOf(flavorName.toUpperCase());
  }
  return new CsvSchema(new File(directory), flavor);
}

  根据model的配置,这个schema 工程类实例化了一个名为SALES的schema。这个schema是org.apache.calcite.adapter.csv.CsvSchema的一个实例,实现了Calcite中的Schema接口。
  一个schema的职责是产生一系列的tables(它也可以列举出子schema和table-function,但这些高级的特性在calcite-example-csv中没有支持)。这些table实现了Calcite的Table接口。CsvSchema生成了一些tables,它们是CsvTable以及CsvTable的子类的实例。
  下面是CsvSchema的一些相关代码,对基类AbstractSchema中的getTableMap()方法进行了重载。

protected Map<String, Table> getTableMap() {
  // Look for files in the directory ending in ".csv", ".csv.gz", ".json",".json.gz".
  File[] files = directoryFile.listFiles(
      new FilenameFilter() {
        public boolean accept(File dir, String name) {
          final String nameSansGz = trim(name, ".gz");
          return nameSansGz.endsWith(".csv")|| nameSansGz.endsWith(".json");
        }
      });
  if (files == null) {
    System.out.println("directory " + directoryFile + " not found");
    files = new File[0];
  }
  // Build a map from table name to table; each file becomes a table.
  final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
  for (File file : files) {
    String tableName = trim(file.getName(), ".gz");
    final String tableNameSansJson = trimOrNull(tableName, ".json");
    if (tableNameSansJson != null) {
      JsonTable table = new JsonTable(file);
      builder.put(tableNameSansJson, table);
      continue;
    }
    tableName = trim(tableName, ".csv");
    final Table table = createTable(file);
    builder.put(tableName, table);
  }
  return builder.build();
}

/** Creates different sub-type of table based on the "flavor" attribute. */
private Table createTable(File file) {
  switch (flavor) {
  case TRANSLATABLE:
    return new CsvTranslatableTable(file, null);
  case SCANNABLE:
    return new CsvScannableTable(file, null);
  case FILTERABLE:
    return new CsvFilterableTable(file, null);
  default:
    throw new AssertionError("Unknown flavor " + flavor);
  }
}

  schema会扫描指定路径,找到所有以".csv”结尾的文件。在本例中,指定路径是 target/test-classes/sales,路径中包含文件EMPS.csv和DEPTS.csv,这两个文件会转换成EMPS和DEPTS表。

猜你喜欢

转载自blog.51cto.com/1196740/2160528