Guoliang Sun created CALCITE-5887:
-------------------------------------
Summary: Trim function can’t work well with
SqlDelegatingConformance
Key: CALCITE-5887
URL: https://issues.apache.org/jira/browse/CALCITE-5887
Project: Calcite
Issue Type: Bug
Components: core
Affects Versions: 1.35.0
Reporter: Guoliang Sun
In Calcite, there are test cases that override this function, e.g.:
SqlFunctionsTest#testTrim.
But the use case ignores the fact that the function is optimized in SQL with
rules, which I'll illustrate with an example below.
Take the following SQL as an example of the expected result: *calcite*
{code:java}
select trim(both 'io' from 'iocalciteio'){code}
I wanted to use Calcite's SQLConformance by way of a custom extension, so I
inherited SqlDelegatingConformance, overrode the allowExtendedTrim method, and
set it when SqlParser was initialized.
The SQL is then optimized by a series of parsing transformations, and at this
point it comes to {*}CoreRules.PROJECT_REDUCE_EXPRESSIONS{*}, which calls
RexExecutorImpl to compute the RexNode expression, reducing the constants by
generating code for optimization purposes. In the
*{color:#172b4d}RexExecutorImpl#compile{color}* method, you can see that the
SqlConformance is written to Default, which will cause the custom
*SqlDelegatingConformance* set earlier to not take effect, see the code below.
{code:java}
private static String compile(RexBuilder rexBuilder, List<RexNode> constExps,
RexToLixTranslator.InputGetter getter, RelDataType rowType) {
final RexProgramBuilder programBuilder =
new RexProgramBuilder(rowType, rexBuilder);
for (RexNode node : constExps) {
programBuilder.addProject(
node, "c" + programBuilder.getProjectList().size());
}
final RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory();
final JavaTypeFactory javaTypeFactory = typeFactory instanceof JavaTypeFactory
? (JavaTypeFactory) typeFactory
: new JavaTypeFactoryImpl(typeFactory.getTypeSystem());
final BlockBuilder blockBuilder = new BlockBuilder();
final ParameterExpression root0_ =
Expressions.parameter(Object.class, "root0");
final ParameterExpression root_ = DataContext.ROOT;
blockBuilder.add(
Expressions.declare(
Modifier.FINAL, root_,
Expressions.convert_(root0_, DataContext.class)));
final SqlConformance conformance = SqlConformanceEnum.DEFAULT;
final RexProgram program = programBuilder.getProgram();
final List<Expression> expressions =
RexToLixTranslator.translateProjects(program, javaTypeFactory,
conformance, blockBuilder, null, null, root_, getter, null);
blockBuilder.add(
Expressions.return_(null,
Expressions.newArrayInit(Object[].class, expressions)));
final MethodDeclaration methodDecl =
Expressions.methodDecl(Modifier.PUBLIC, Object[].class,
BuiltInMethod.FUNCTION1_APPLY.method.getName(),
ImmutableList.of(root0_), blockBuilder.toBlock());
String code = Expressions.toString(methodDecl);
if (CalciteSystemProperty.DEBUG.value()) {
Util.debugCode(System.out, code);
}
return code;
} {code}
When you get {*}SqlConformanceEnum.DEFAULT{*}, you get a MethodCallExpression
in the *TrimImplementor#implementSafe* method that doesn't allow Trim to do
multiple character operations on SQL, so Calcite throws an exception in the
SqlFunctions#trim method.
*{color:#de350b}Trim error: trim character must be exactly 1 character{color}*
Coincidentally, this exception is caught by the try catch in the
{color:#172b4d}*RexExecutable#reduce*{color} method, which returns the initial
unoptimized expression, with no exception output, and after the final execution
is complete, you can only see that the result is not as expected.
By the way, I don't think it's reasonable to output the exception message, if
the optimization fails, at least it should be logged for troubleshooting.
Regarding solution ideas, I can think of two ways at the moment and would like
to know what the community thinks, or if the community thinks this is a problem?
* Change SQLConformance to global
* Change CalciteConnectionConfigImpl to global
This would allow you to get the SQLConformance information that has been set in
the configuration elsewhere, there are many places in Calcite where DEFAULT may
be written because you can't get the configuration information.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)