Apache Calcite - регистрация пользовательских функций для использования в RelBuilder - PullRequest
0 голосов
/ 07 октября 2019

Образцы UOT-юнитов OOTB (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();

, но когда я пытаюсь использовать вышеуказанную функцию 'length' в builder.call, он выдает исключение -

java.lang.RuntimeException: cannot translate call STRLEN($t3)

builder извлекает реализацию этих функций из приватной карты класса RexImpTable. В этом классе нет открытых / защищенных apis для добавления значений к этой карте.

Подскажите, пожалуйста, как зарегистрировать любой UDF в Calcite и использовать его в RelBuilder?

1 Ответ

0 голосов
/ 11 октября 2019

Комментарий к кальциту jira для моего вопроса здесь отвечает на вопрос одним подходом -

Это потому, что SqlStdOperatorTable.instance () выполнил некоторую работу по инициализации для зарегистрированных функций,Так что вызов #register после того, как он не будет работать, как ожидалось. Правильный способ - использовать ListSqlOperatorTable и связать его с StdSqlOperatorTable с ChainedSqlOperatorTable, код пресуда может выглядеть так:

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));
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...