I need to add a user-defined function to Calcite that takes an integer as a parameter and returns an integer.
public class SquareFunction {
public int eval(int a) {
return a*a;
}
}
and the relevant code that creates a schema and adds the function is
SchemaPlus rootSchema = Frameworks.createRootSchema(false);
rootSchema.add("SQUARE_FUNC",
ScalarFunctionImpl.create(SquareFunction.class,"eval");
But a simple SQL like
select SQUARE_FUNC(1) from test;
fails during the validation with the following message:
No match found for function signature SQUARE_FUNC(<NUMERIC>)
The stack trace is:
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
I followed the Calcite's UdfTest.testUserDefinedFunctionInView
implementation but still couldn't make it work. What am I doing wrong?
Is
SquareFunction
an inner class? If so, try making it static.If that doesn't work, try making
eval
a static method.As CalciteCatalogReader object requires context, There is another way to register custom Functions.
How does Calcite validates that a function exists?
As I known,
SqlOperatorTable
defines a directory interface for enumerating and looking up SQL operators and functions, andlookupOperatorOverloads
retrieves a list of operators with a given name and syntax.For the
Frameworks
, the defaultSqlOperatorTable
ofFrameworkConfig
isSqlStdOperatorTable.instance()
. But the function you added only searched atCalciteCatalogReader
. So following code can work: