You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by ru...@apache.org on 2020/08/28 07:17:08 UTC

[calcite] branch master updated: [CALCITE-4195] Cast between types with different collators must be evaluated as not monotonic

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 <ru...@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);