This is an automated email from the ASF dual-hosted git repository. rubenql pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push: new 425f170 [CALCITE-4195] Cast between types with different collators must be evaluated as not monotonic 425f170 is described below commit 425f170a9b0589cb7f693e93cbcd044e9ab98c75 Author: rubenada <rube...@gmail.com> AuthorDate: Wed Aug 26 15:09:02 2020 +0100 [CALCITE-4195] Cast between types with different collators must be evaluated as not monotonic --- .../apache/calcite/sql/fun/SqlCastFunction.java | 24 +++++++++--- .../enumerable/EnumerableStringComparisonTest.java | 43 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java index 393bf56..76e7422 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java @@ -42,6 +42,9 @@ import org.apache.calcite.sql.validate.SqlValidatorImpl; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; +import java.text.Collator; +import java.util.Objects; + import static org.apache.calcite.util.Static.RESOURCE; /** @@ -188,11 +191,22 @@ public class SqlCastFunction extends SqlFunction { } @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) { - RelDataTypeFamily castFrom = call.getOperandType(0).getFamily(); - RelDataTypeFamily castTo = call.getOperandType(1).getFamily(); - if (castFrom instanceof SqlTypeFamily - && castTo instanceof SqlTypeFamily - && nonMonotonicCasts.containsEntry(castFrom, castTo)) { + final RelDataType castFromType = call.getOperandType(0); + final RelDataTypeFamily castFromFamily = castFromType.getFamily(); + final Collator castFromCollator = castFromType.getCollation() == null + ? null + : castFromType.getCollation().getCollator(); + final RelDataType castToType = call.getOperandType(1); + final RelDataTypeFamily castToFamily = castToType.getFamily(); + final Collator castToCollator = castToType.getCollation() == null + ? null + : castToType.getCollation().getCollator(); + if (!Objects.equals(castFromCollator, castToCollator)) { + // Cast between types compared with different collators: not monotonic. + return SqlMonotonicity.NOT_MONOTONIC; + } else if (castFromFamily instanceof SqlTypeFamily + && castToFamily instanceof SqlTypeFamily + && nonMonotonicCasts.containsEntry(castFromFamily, castToFamily)) { return SqlMonotonicity.NOT_MONOTONIC; } else { return call.getOperandMonotonicity(0); diff --git a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableStringComparisonTest.java b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableStringComparisonTest.java index 1027807..e814713 100644 --- a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableStringComparisonTest.java +++ b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableStringComparisonTest.java @@ -22,7 +22,13 @@ import org.apache.calcite.config.CalciteConnectionProperty; import org.apache.calcite.config.Lex; import org.apache.calcite.jdbc.JavaCollation; import org.apache.calcite.plan.RelOptPlanner; +import org.apache.calcite.plan.RelOptUtil; +import org.apache.calcite.plan.hep.HepPlanner; +import org.apache.calcite.plan.hep.HepProgram; +import org.apache.calcite.plan.hep.HepProgramBuilder; +import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.rules.CoreRules; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.runtime.Hook; @@ -31,6 +37,7 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.test.CalciteAssert; import org.apache.calcite.test.JdbcTest; +import org.apache.calcite.test.RelBuilderTest; import org.apache.calcite.tools.RelBuilder; import org.apache.calcite.util.Util; @@ -45,6 +52,9 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.EQUALS; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.GREATER_THAN; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.LESS_THAN; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_EQUALS; +import static org.apache.calcite.test.Matchers.isLinux; + +import static org.hamcrest.MatcherAssert.assertThat; /** * Test cases for @@ -159,6 +169,39 @@ class EnumerableStringComparisonTest { + "name=Marketing; name0=Marketing"); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-4195">[CALCITE-4195] + * Cast between types with different collators must be evaluated as not monotonic</a>. */ + @Test void testCastDifferentCollationShouldNotApplySortProjectTranspose() { + final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build()); + final RelNode relNode = relBuilder + .values( + createRecordVarcharSpecialCollation(relBuilder), + "Legal", "presales", "hr", "Administration", "MARKETING") + .project( + relBuilder.cast(relBuilder.field("name"), SqlTypeName.VARCHAR)) + .sort( + relBuilder.field(1, 0, 0)) + .build(); + + // Cast to a type with a different collation, and then sort; + // in this scenario SORT_PROJECT_TRANSPOSE must not be applied. + final HepProgram program = new HepProgramBuilder() + .addRuleInstance(CoreRules.SORT_PROJECT_TRANSPOSE) + .build(); + final HepPlanner hepPlanner = new HepPlanner(program); + hepPlanner.setRoot(relNode); + final RelNode output = hepPlanner.findBestExp(); + final String planBefore = RelOptUtil.toString(relNode); + final String planAfter = RelOptUtil.toString(output); + final String expected = + "LogicalSort(sort0=[$0], dir0=[ASC])\n" + + " LogicalProject(name=[CAST($0):VARCHAR NOT NULL])\n" + + " LogicalValues(tuples=[[{ 'Legal' }, { 'presales' }, { 'hr' }, { 'Administration' }, { 'MARKETING' }]])\n"; + assertThat(planBefore, isLinux(expected)); + assertThat(planAfter, isLinux(expected)); + } + @Test void testStringComparison() { testStringComparison("a", "A", LESS_THAN, true); testStringComparison("a", "A", GREATER_THAN, false);