Apache Calcite入门

什么是Calcite

按照我自己的理解,Apache是一个SQL引擎,通过它,我们可以对各种数据源进行sql化的查询,比如说elasticsearch、file、redis等等。

1 Apache Calcite是什么

首先我们来看下官网对calcite的描述

Apache Calcite is a dynamic data management framework.
It contains many of the pieces that comprise a typical database management system, but omits some key functions: storage of data, algorithms to process data, and a repository for storing metadata.
Calcite intentionally stays out of the business of storing and processing data. As we shall see, this makes it an excellent choice for mediating between applications and one or more data storage locations and data processing engines. It is also a perfect foundation for building a database: just add data.

如果大家不想看那么长串的英文,那我就通过自己的理解来给大家总结一下上面这些话:
Apache Calcite是一个动态的数据管理的框架,内置了很多的数据库系统的管理,比如说ES、Redis、CSV、FileSystem等。但是它并不参与存储,也就是说,Calcite只参与数据的处理。
正是因为它不参与存储,calcite成为了一个通用的不同存储之间的沟通媒介。如果你需要使用Apache Calcite对某个数据库进行处理,那么你唯一要做的就是往Calcite中添加数据即可。

2 例子

下面我们将带领大家来看一个官网的例子:
从CSV中读取数据:
首先github下载源代码 https://github.com/apache/calcite.git

cd calcite/example/csv
./sqlline
sqlline> !connect jdbc:calcite:model=src/test/resources/model.json admin admin

即可进入sql模式.
那么,我们进入model.json文件来一探究竟:

{
  "version": "1.0",
  "defaultSchema": "SALES",
  "schemas": [
    {
      "name": "SALES",
      "type": "custom",
      "factory": "org.apache.calcite.adapter.csv.CsvSchemaFactory",
      "operand": {
        "directory": "sales"
      }
    }
  ]
}

读取该文件

那么在哪里读取的这个json呢,ModelHandler这个里面会读取对应的json,然后创建对应的factory,代码在visit()方法中,如下:

public void visit(JsonCustomSchema jsonSchema) {
    try {
      final SchemaPlus parentSchema = currentMutableSchema("sub-schema");

	// 创建org.apache.calcite.adapter.csv.CsvSchemaFactory
      final SchemaFactory schemaFactory =
          AvaticaUtils.instantiatePlugin(SchemaFactory.class,
              jsonSchema.factory);

	// 生成CsvSchema
      final Schema schema =
          schemaFactory.create(
              parentSchema, jsonSchema.name, operandMap(jsonSchema, jsonSchema.operand));
      final SchemaPlus schemaPlus = parentSchema.add(jsonSchema.name, schema);
      populateSchema(jsonSchema, schemaPlus);
    } catch (Exception e) {
      throw new RuntimeException("Error instantiating " + jsonSchema, e);
    }
  }

下面我们看org.apache.calcite.adapter.csv.CsvSchemaFactory这个类,这个类有一个create方法,这个方法会返回一个schema,我们看之前的visit方法,里面有调用该方法去生成一个schema。

CsvSchema

这个类继承了AbstractSchema,重写了getTableMap方法,该方法会返回对应的table名称。
该方法调用了createTableMap方法,实际上是从文件中读取数据生成table,接下来我们我们只需要继续看table实现即可。

CsvScannableTable

我们首先举一个最简单的例子,即CsvScannableTable,它继承了CsvTable,实现了ScanableTable

public class CsvScannableTable extends CsvTable
    implements ScannableTable {

...
	}

我们先看它的父类,CsvTable,需要关注两个方法,第一个是getRowType,第二个是getFieldTypes
首先我们看getRowType这个方法是重载的我们的父类方法,这个方法的用途是需要返回表的字段名,及其类型。

本质上,这两个方法实际上都调用了CsvEnumerator.deduceRowType这个方法,一个返回的是RelDataType,一个是赋值fieldTypes。

 // 获取表的字段名及其类型。
@Override public RelDataType getRowType(RelDataTypeFactory typeFactory) {
    if (protoRowType != null) {
      return protoRowType.apply(typeFactory);
    }
    if (rowType == null) {
      rowType = CsvEnumerator.deduceRowType((JavaTypeFactory) typeFactory, source,
          null, isStream());
    }
    return rowType;
  }

  // 返回表的字段的类型。
  public List<RelDataType> getFieldTypes(RelDataTypeFactory typeFactory) {
    if (fieldTypes == null) {
      fieldTypes = new ArrayList<>();
      CsvEnumerator.deduceRowType((JavaTypeFactory) typeFactory, source,
          fieldTypes, isStream());
    }
    return fieldTypes;
  }

CsvEnumerator.deduceRowType

接下来我们看CsvEnumerator.deduceRowType这个方法,别看这个方法很长,实际上做的事情很简单:

  1. 从csv中读取第一行,获取字符串
  2. 通过获取的数据,判断类型,构建出fieldType
  3. 创建struct类型的RelDataType并返回。
  4. 如果传入了fieldTypes,则把类型添加到这个list中。
public static RelDataType deduceRowType(JavaTypeFactory typeFactory,
      Source source, @Nullable List<RelDataType> fieldTypes, Boolean stream) {
    final List<RelDataType> types = new ArrayList<>();
    final List<String> names = new ArrayList<>();
    if (stream) {
      names.add(FileSchemaFactory.ROWTIME_COLUMN_NAME);
      types.add(typeFactory.createSqlType(SqlTypeName.TIMESTAMP));
    }
    try (CSVReader reader = openCsv(source)) {
      String[] strings = reader.readNext();
      if (strings == null) {
        strings = new String[]{"EmptyFileHasNoColumns:boolean"};
      }
      for (String string : strings) {
        final String name;
        final RelDataType fieldType;
        final int colon = string.indexOf(':');
        if (colon >= 0) {
          name = string.substring(0, colon);
          String typeString = string.substring(colon + 1);
          Matcher decimalMatcher = DECIMAL_TYPE_PATTERN.matcher(typeString);
          if (decimalMatcher.matches()) {
            int precision = Integer.parseInt(decimalMatcher.group(1));
            int scale = Integer.parseInt(decimalMatcher.group(2));
            fieldType = parseDecimalSqlType(typeFactory, precision, scale);
          } else {
            switch (typeString) {
            case "string":
              fieldType = toNullableRelDataType(typeFactory, SqlTypeName.VARCHAR);
             ... 省略代码
            default:
              LOGGER.warn(
                  "Found unknown type: {} in file: {} for column: {}. Will assume the type of "
                      + "column is string.",
                  typeString, source.path(), name);
              fieldType = toNullableRelDataType(typeFactory, SqlTypeName.VARCHAR);
              break;
            }
          }
        } else {
          name = string;
          fieldType = typeFactory.createSqlType(SqlTypeName.VARCHAR);
        }
        names.add(name);
        types.add(fieldType);
		// 如果传入了fieldTypes,则添加进去
        if (fieldTypes != null) {
          fieldTypes.add(fieldType);
        }
      }
    } catch (IOException e) {
      // ignore
    }
    if (names.isEmpty()) {
      names.add("line");
      types.add(typeFactory.createSqlType(SqlTypeName.VARCHAR));
    }
    return typeFactory.createStructType(Pair.zip(names, types));
  }


接下来我们把目光返回到CsvScannableTable,看scan方法,这个方法需要返回一个Enumerable的类型。
其中有一个arrayConverter,这个类型,这个是

@Override public Enumerable<@Nullable Object[]> scan(DataContext root) {
    JavaTypeFactory typeFactory = root.getTypeFactory();
    // 调用的父类的之前getFieldTypes,我们上面第四点已经说过,返回的是表的字段的类型。
    final List<RelDataType> fieldTypes = getFieldTypes(typeFactory);
    final List<Integer> fields = ImmutableIntList.identity(fieldTypes.size());
    final AtomicBoolean cancelFlag = DataContext.Variable.CANCEL_FLAG.get(root);
    return new AbstractEnumerable<@Nullable Object[]>() {
      @Override public Enumerator<@Nullable Object[]> enumerator() {
        return new CsvEnumerator<>(source, cancelFlag, false, null,
            CsvEnumerator.arrayConverter(fieldTypes, fields, false));
      }
    };
  }

我们上面CsvEnumerator.deduceRowType 中,有个fieldTypes,就是这个fieldTypes,这个fieldTypes是用来对数据进行转换的。具体使用在ArrayRowConverter

ArrayRowConverter

convert函数

通过sqlType,转换该行对应的字段到对应的类型。

convertRow函数

list调用convert函数。
具体调用在CsvEnumerator中的moveNext()函数,而moveNext函数则在sql执行的时候,获取数据的时候调用。所以在获取数据的时候,会调用convert函数去自适应对应的字段,比如说数据里面是string,但是sql定义的时候是int,则会去尝试转换。

Driver

关于Driver这个类,实际上是实现了jdbc的driver类,具体是怎么实现的,如果后面有时间,我会出一篇文章。

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是学习 Apache Calcite 的一些步骤和资源: 1. 了解 Apache Calcite - 了解 Apache Calcite 的基本概念和特点。可以查看 Apache Calcite 官方网站和文档,了解 Calcite 的功能和用途。 2. 学习 SQL - Apache Calcite 是一个 SQL 解析器和查询优化器,因此了解 SQL 语言的基本语法和特性是非常重要的。可以阅读 SQL 教程或书籍来学习 SQL。 3. 安装和配置 Apache Calcite - 从 Apache Calcite 的官方网站下载最新版本的 Calcite,并按照官方文档的指导进行安装和配置。 4. 编写 SQL 查询 - 编写一些简单的 SQL 查询并在 Apache Calcite 中运行它们,可以使用 Calcite 的命令行界面,也可以在 Java 应用程序中集成 Calcite。 5. 学习 Apache Calcite 的 API - 学习 Apache Calcite 的 API,并尝试使用它们来开发自己的应用程序。可以查看官方文档和示例代码来学习 Calcite 的 API。 6. 参与社区 - 加入 Apache Calcite 的邮件列表和社区,与其他开发者交流,了解 Calcite 的最新动态和发展方向。 推荐一些 Apache Calcite 的学习资源: - Apache Calcite 官方网站:https://calcite.apache.org/ - Apache Calcite 官方文档:https://calcite.apache.org/docs/ - Apache Calcite 示例代码:https://github.com/apache/calcite/tree/master/example - Apache Calcite 的邮件列表:https://calcite.apache.org/mailing-lists.html - SQL 教程:https://www.w3schools.com/sql/ - 《Apache Calcite: A Foundational Framework for Optimized Query Processing》一书,由 Apache Calcite 的核心开发者编写,介绍了 Apache Calcite 的设计和实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值