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)