Apache Calcite——新增动态UDF支持

简介

通过Apache Calcite支持UDF有两种方式:

  1. 动态支持:直接通过执行时候将UDF的函数名以及类名传进去,这种方式解析的时候可以在schema中获取到UDF的信息,进而通过Sql校验(这次主要描述这种用法的使用过程以及注意事项)。
  2. 静态支持:类似Sql中已有的sum、abs等内置函数,通过侵入式的方式将函数添加到其中。

使用过程

这里以JdbcExample为准,介绍整体的使用方式。

加在特定的Schema中

为特定的Schema添加UDF支持,调用时统一使用

"schemaName".udfName(parameter...)

代码实例

Class.forName("org.apache.calcite.jdbc.Driver");
Connection connection =DriverManager.getConnection("jdbc:calcite:");
// 这里是扩展Connection的一种方式,值得学习,能够突破JDBC的传统接口的限制
CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class);
SchemaPlus rootSchema = calciteConnection.getRootSchema();
rootSchema.add("hr", new ReflectiveSchema(new Hr()));
// 这里将UDF注册到Schema Footmart 中
SchemaPlus schemaPlus = rootSchema.add("foodmart", new ReflectiveSchema(new Foodmart()));
schemaPlus.add("DATETOLOCALDATE", ScalarFunctionImpl.create(Udf.class, "dateToLocalDate"));

加在整体的Schema中

整体的Schema添加UDF后,调用时可以直接调用

udfName(parameter...)

代码实例

Class.forName("org.apache.calcite.jdbc.Driver");
Connection connection =DriverManager.getConnection("jdbc:calcite:");
// 这里是扩展Connection的一种方式,值得学习,能够突破JDBC的传统接口的限制
CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class);
SchemaPlus rootSchema = calciteConnection.getRootSchema();
rootSchema.add("hr", new ReflectiveSchema(new Hr()));
SchemaPlus schemaPlus = rootSchema.add("foodmart", new ReflectiveSchema(new Foodmart()));
// 这里将UDF注册到所有Schema 中
rootSchema.add("DATETOLOCALDATE", ScalarFunctionImpl.create(Udf.class, "dateToLocalDate"));

整体Schema执行Sql

"select \"DATETOLOCALDATE\"(121)"

附加UDF代码.内置函数的实现

public class Udf {
  public static LocalDate dateToLocalDate(int date) {
    int y0 = (int) DateTimeUtils.unixDateExtract(TimeUnitRange.YEAR, date);
    int m0 = (int) DateTimeUtils.unixDateExtract(TimeUnitRange.MONTH, date);
    int d0 = (int) DateTimeUtils.unixDateExtract(TimeUnitRange.DAY, date);
    return LocalDate.of(y0, m0, d0);
  }
}

注意事项

在默认的JdbcExample中如果是小写的Schema与Udf名,需要将名字用“”引用起来,否则会被转化为大写,导致函数名匹配不到,或者通过properties设置连接属性:CalciteConnectionProperty所有支持的属性都在这个类中,将connection设置为大小写不敏感的或者设置QUOTED相关属性不去将关键字转化为特定大小写。

原理分析

当执行UDF注册,其实是通过CalciteSchema将相应的UDF属性保存在Schema的functionNames以及functionMap中。

  private FunctionEntry add(String name, Function function) {
    final FunctionEntryImpl entry =
        new FunctionEntryImpl(this, name, function);
    functionMap.put(name, entry);
    functionNames.add(name);
    // 这个是无参构造函数
    if (function.getParameters().isEmpty()) {
      nullaryFunctionMap.put(name, entry);
    }
    return entry;
  }

具体的Sql通过Statement执行,最终调用Connection的方法,这里不做详述,后续有机会另写一遍记录一下。这里具体看Sql验证流程,因为Connecion连接的时候参数配置可能导致找不到特定方法。提示如:

Caused by: org.apache.calcite.runtime.CalciteContextException: From line 1, column 8 to line 1, column 29: No match found for function signature datetolocaldate(<NUMERIC>)

具体看一下如何找到相应的UDF定义并通过验证的,Calcite是通过CalciteCatalogReader提供元数据信息进行Sql验证的:

这个CalciteCatalogReader在初始化时指定了这个是否是大小写敏感的,看一下构造方法:
在这里插入图片描述
查看config.caseSensitive的实现,可以发现、

  @Override public <T> @PolyNull T parserFactory(Class<T> parserFactoryClass,
      @PolyNull T defaultParserFactory) {
    return CalciteConnectionProperty.PARSER_FACTORY.wrap(properties)
        .getPlugin(parserFactoryClass, defaultParserFactory);
  }

上述的Properties就是建立connection传递进来的参数,因此可以通过上述通过properties的形式,将相应的参数传递进来,例如:

    Class.forName("org.apache.calcite.jdbc.Driver");
    Properties properties = new Properties();
    // 传递这个conntion是否是大小写敏感的
    properties.setProperty("caseSensitive","false");
    Connection connection =
        DriverManager.getConnection("jdbc:calcite:",properties);

接下来继续看是如何做函数名校验的。

在这里插入图片描述
这里可以看到在做函数验证时,从两个地方进行验证,第一个是Calcite的内置函数(静态函数),第二个是Schema的元数据(也就是通过动态注册的UDF)。这里我们主要看一下CalciteCatalogReader的lookupOperatorOverloads实现,主要的:

// 主要通过getFunctionsFrom获取元数据
getFunctionsFrom(opName.names)
        .stream()
        .filter(predicate)
        .map(function -> toOp(opName, function))
        .forEachOrdered(operatorList::add);

具体的实现如下:

private Collection<org.apache.calcite.schema.Function> getFunctionsFrom(
      List<String> names) {
      // 用来保存结果集
    final List<org.apache.calcite.schema.Function> functions2 =
        new ArrayList<>();
    final List<List<String>> schemaNameList = new ArrayList<>();
    // function调用是否前面使用了schemaName
    if (names.size() > 1) {
      // Name qualified: ignore path. But we do look in "/catalog" and "/",
      // the last 2 items in the path.
      if (schemaPaths.size() > 1) {
        schemaNameList.addAll(Util.skip(schemaPaths));
      } else {
        schemaNameList.addAll(schemaPaths);
      }
    } else {
      for (List<String> schemaPath : schemaPaths) {
        CalciteSchema schema =
            SqlValidatorUtil.getSchema(rootSchema, schemaPath, nameMatcher);
        if (schema != null) {
          schemaNameList.addAll(schema.getPath());
        }
      }
    }
    // schemaNameList为所属的Schema
    for (List<String> schemaNames : schemaNameList) {
      CalciteSchema schema =
          SqlValidatorUtil.getSchema(rootSchema,
              Iterables.concat(schemaNames, Util.skipLast(names)), nameMatcher);
      // 从schema中获取所属的函数
      if (schema != null) {
        final String name = Util.last(names);
        boolean caseSensitive = nameMatcher.isCaseSensitive();
        functions2.addAll(schema.getFunctions(name, caseSensitive));
      }
    }
    // 如果functions2 size为0.则表示没有获取到相应的方法,验证失败。
    return functions2;
  }

这里已经完成了Function的验证流程。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
以下是学习 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 的设计和实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NobiGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值