为了账号安全,请及时绑定邮箱和手机立即绑定

Apache Calcite - 注册 UDF 以在 RelBuilder 中使用

Apache Calcite - 注册 UDF 以在 RelBuilder 中使用

回首忆惘然 2023-09-27 17:20:24
OOTB 示例 udf junits (UdfTest.java) 使用虚拟 jdbc 架构,并且不显示 RelBuilder api 的用法。我正在注册一个简单的 UDF,它返回输入字符串的长度。我已经创建了 SqlFunction 并在 SqlStdOperatorTable 中注册了相同的内容 -SqlFunction length = new SqlFunction("STRLEN",                SqlKind.OTHER_FUNCTION,                ReturnTypes.INTEGER,                null,                OperandTypes.STRING,                SqlFunctionCategory.USER_DEFINED_FUNCTION);SqlStdOperatorTable sqlStdOperatorTable = SqlStdOperatorTable.instance();sqlStdOperatorTable.register(length);并用它来创建 FrameworkConfig -FrameworkConfig frameworkConfig = Frameworks.newConfigBuilder()                .parserConfig(SqlParser.Config.DEFAULT)                .defaultSchema(connection.getRootSchema().getSubSchema("SYSTEM"))                .programs(Programs.sequence(Programs.ofRules(Programs.RULE_SET), Programs.CALC_PROGRAM))                .operatorTable(sqlStdOperatorTable)                .build();现在我可以使用预定义的 sql 函数substr,例如在 SqlStringLengthFunction 类中定义的函数,其中包含以下部分:RelNode udfRelNode = builder                .scan("EMP")                .project(builder.call(new SqlStringLengthFunction(),builder.literal("SampleString"), builder.literal(3))                .build();PreparedStatement statement = RelRunners.run(udfRelNode);ResultSet resultSet = statement.executeQuery();但是当我在 builder.call 中尝试使用上述函数“length”时,它会抛出异常 -java.lang.RuntimeException: cannot translate call STRLEN($t3)构建器从类中的私有映射中获取这些函数的实现RexImpTable。此类中没有公开/受保护的 API 来向该映射添加值。您能否指导如何使用 Calcite 注册任何 UDF 并将其与 RelBuilder 一起使用?
查看完整描述

1 回答

?
红颜莎娜

TA贡献1842条经验 获得超12个赞


这是因为 SqlStdOperatorTable.instance() 为注册的函数做了一些初始化工作。因此,在 #register 之后调用它不会按预期工作。正确的方法是使用 ListSqlOperatorTable 并将其与带有 ChainedSqlOperatorTable 的 StdSqlOperatorTable 链接起来,预置代码可能如下所示:

ListSqlOperatorTable listOpTable = new ListSqlOperatorTable();

listOpTable.add(my_udf);

ChainedSqlOperatorTable chainedOpTable = ChainedSqlOperatorTable.of(listOpTable, SqlStdOperatorTable.instance());

// then use this chainedOpTable


// If you want to use a special dialect operators, you can code like this

SqlOperatorTable optable = SqlLibraryOperatorTableFactory.INSTANCE

  .getOperatorTable(SqlLibrary.STANDARD, SqlLibrary.POSTGRESQL);

我用以下方法解决了我的问题 -


// methods containing the udf logic 

public static class MyUdf1 {

        public Integer eval(String a) {

            return a.length();

        }

    }



@Test

    public void test1() throws SQLException, ClassNotFoundException {


        CalciteConnection connection = MyTests.getCalciteConnection();


        final String functionName = "STR_LEN";

        final ScalarFunction udfLengthFunction = ScalarFunctionImpl.create(Types.lookupMethod(MyUdf1.class, "eval", String.class));

        connection.getRootSchema().getSubSchema("SYSTEM").add(functionName, udfLengthFunction);


        FrameworkConfig frameworkConfig = Frameworks.newConfigBuilder()

                .parserConfig(SqlParser.Config.DEFAULT)

                .defaultSchema(connection.getRootSchema().getSubSchema("SYSTEM"))

                .programs(Programs.sequence(Programs.ofRules(Programs.RULE_SET), Programs.CALC_PROGRAM))

                .build();


        SqlIdentifier udfLengthIdentifier = new SqlIdentifier(Collections.singletonList(functionName), null, SqlParserPos.ZERO, null);

        final SqlOperator strLenOperator = new SqlUserDefinedFunction(udfLengthIdentifier, ReturnTypes.INTEGER, null, OperandTypes.STRING, null, udfLengthFunction);


        final RelBuilder builder = RelBuilder.create(frameworkConfig);

        RelNode udfRelNode = builder

                .scan("EMP")

                .project(builder.call(strLenOperator, builder.literal("SampleString")))

                .build();


        ResultSet set = RelRunners.run(udfRelNode).executeQuery();

        set.next();

        System.out.println(set.getString(1));

    }


查看完整回答
反对 回复 2023-09-27
  • 1 回答
  • 0 关注
  • 138 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信