Добавление пользовательской функции в Calcite
Мне нужно добавить пользовательскую функцию в Calcite, которая принимает целое число в качестве параметра и возвращает целое число.
public class SquareFunction {
public int eval(int a) {
return a*a;
}
}
и соответствующий код, который создает схему и добавляет функцию
SchemaPlus rootSchema = Frameworks.createRootSchema(false);
rootSchema.add("SQUARE_FUNC",
ScalarFunctionImpl.create(SquareFunction.class,"eval");
Но простой SQL, как
select SQUARE_FUNC(1) from test;
не удается во время проверки со следующим сообщением:
No match found for function signature SQUARE_FUNC(<NUMERIC>)
Трассировка стека:
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
at org.apache.calcite.runtime.Resources$ExInstWithCause.ex(Resources.java:463)
at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:804)
at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:789)
at org.apache.calcite.sql.validate.SqlValidatorImpl.newValidationError(SqlValidatorImpl.java:4386)
at org.apache.calcite.sql.validate.SqlValidatorImpl.handleUnresolvedFunction(SqlValidatorImpl.java:1670)
at org.apache.calcite.sql.SqlFunction.deriveType(SqlFunction.java:278)
at org.apache.calcite.sql.SqlFunction.deriveType(SqlFunction.java:223)
at org.apache.calcite.sql.validate.SqlValidatorImpl$DeriveTypeVisitor.visit(SqlValidatorImpl.java:4965)
at org.apache.calcite.sql.validate.SqlValidatorImpl$DeriveTypeVisitor.visit(SqlValidatorImpl.java:1)
at org.apache.calcite.sql.SqlCall.accept(SqlCall.java:137)
at org.apache.calcite.sql.validate.SqlValidatorImpl.deriveTypeImpl(SqlValidatorImpl.java:1586)
at org.apache.calcite.sql.validate.SqlValidatorImpl.deriveType(SqlValidatorImpl.java:1571)
at org.apache.calcite.sql.validate.SqlValidatorImpl.expandSelectItem(SqlValidatorImpl.java:453)
at org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelectList(SqlValidatorImpl.java:3668)
at org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelect(SqlValidatorImpl.java:3186)
at org.apache.calcite.sql.validate.SelectNamespace.validateImpl(SelectNamespace.java:60)
at org.apache.calcite.sql.validate.AbstractNamespace.validate(AbstractNamespace.java:84)
at org.apache.calcite.sql.validate.SqlValidatorImpl.validateNamespace(SqlValidatorImpl.java:937)
at org.apache.calcite.sql.validate.SqlValidatorImpl.validateQuery(SqlValidatorImpl.java:918)
at org.apache.calcite.sql.SqlSelect.validate(SqlSelect.java:220)
at org.apache.calcite.sql.validate.SqlValidatorImpl.validateScopedExpression(SqlValidatorImpl.java:893)
at org.apache.calcite.sql.validate.SqlValidatorImpl.validate(SqlValidatorImpl.java:603)
at org.apache.calcite.prepare.PlannerImpl.validate(PlannerImpl.java:188) ... 26 more
Я следовал за кальцитом UdfTest.testUserDefinedFunctionInView
реализация, но до сих пор не может заставить его работать. Что я делаю неправильно?
2 ответа
Как Calcite подтверждает, что функция существует?
Как я знаю, SqlOperatorTable
определяет интерфейс каталога для перечисления и поиска операторов и функций SQL, и lookupOperatorOverloads
извлекает список операторов с заданным именем и синтаксисом.
Для Frameworks
, по умолчанию SqlOperatorTable
из FrameworkConfig
является SqlStdOperatorTable.instance()
, Но функция, которую вы добавили только в поиске CalciteCatalogReader
, Так что следующий код может работать:
public class UdfTest {
private static final String SQL = "select SQUARE_FUNC(1)";
private static final String FUN_NAME = "SQUARE_FUNC";
public static void main(String[] args) throws Exception {
useFramworksExec();
}
private static void useFramworksExec() throws Exception {
CalciteSchema rootSchema = CalciteSchema.createRootSchema(false, false);
SchemaPlus schema = rootSchema.plus();
schema.add(FUN_NAME, ScalarFunctionImpl.create(SquareFunction.class, "eval"));
CalciteCatalogReader reader = new CalciteCatalogReader(rootSchema,
SqlParser.Config.DEFAULT.caseSensitive(),
rootSchema.path(null),
new JavaTypeFactoryImpl());
FrameworkConfig config = Frameworks.newConfigBuilder().operatorTable(reader).defaultSchema(schema).build();
Planner planner = Frameworks.getPlanner(config);
SqlNode ast = planner.parse(SQL);
SqlNode validatedAst = planner.validate(ast);
System.out.println(validatedAst.toString());
}
}
Является SquareFunction
внутренний класс? Если это так, попробуйте сделать его статичным.
Если это не сработает, попробуйте сделать eval
статический метод.
Поскольку объект CalciteCatalogReader требует контекста, существует другой способ зарегистрировать пользовательские функции.
SqlFunction countRelation = new SqlFunction("COUNT_RELATION",
SqlKind.OTHER_FUNCTION,
ReturnTypes.INTEGER,
null,
OperandTypes.STRING,
SqlFunctionCategory.NUMERIC);
SqlStdOperatorTable sqlStdOperatorTable = SqlStdOperatorTable.instance();
sqlStdOperatorTable.register(countRelation);
this.frameworkConfig = Frameworks.newConfigBuilder()
.parserConfig(parserConfig)
.defaultSchema(schemaPlus)
.programs(Programs.sequence(Programs.ofRules(Programs.RULE_SET), Programs.CALC_PROGRAM))
.traitDefs(traitDefs)
.operatorTable(sqlStdOperatorTable)
.build();