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

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 一起使用?


回首忆惘然
浏览 139回答 1
1回答

红颜莎娜

这是因为 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 thisSqlOperatorTable 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));    }
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java