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

[calcite] branch master updated: [CALCITE-3898] RelOptPredicateList may generate incorrect map of constant values

This is an automated email from the ASF dual-hosted git repository.

jcamacho 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 c3147f4  [CALCITE-3898] RelOptPredicateList may generate incorrect map of constant values
c3147f4 is described below

commit c3147f477f843d283c43d1e858534d58a7174544
Author: Jesus Camacho Rodriguez <jc...@apache.org>
AuthorDate: Mon Apr 6 14:18:08 2020 -0700

    [CALCITE-3898] RelOptPredicateList may generate incorrect map of constant values
    
    Close apache/calcite#1899
---
 .../main/java/org/apache/calcite/rex/RexUtil.java  | 43 +++++++++++++++++++--
 .../org/apache/calcite/rex/RexProgramTest.java     | 44 +++++++++++++++++++++-
 2 files changed, 82 insertions(+), 5 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index c53a7ea..7d5bcfc 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -422,7 +422,7 @@ public class RexUtil {
         // Convert "CAST(c) = literal" to "c = literal", as long as it is a
         // widening cast.
         final RexNode operand = ((RexCall) left).getOperands().get(0);
-        if (canAssignFrom(left.getType(), operand.getType())) {
+        if (canAssignFrom(left.getType(), operand.getType(), rexBuilder.getTypeFactory())) {
           final RexNode castRight =
               rexBuilder.makeCast(operand.getType(), constant);
           if (castRight instanceof RexLiteral) {
@@ -454,15 +454,50 @@ public class RexUtil {
    *   <li>{@code canAssignFrom(BIGINT, VARCHAR)} returns {@code false}</li>
    * </ul>
    */
-  private static boolean canAssignFrom(RelDataType type1, RelDataType type2) {
+  private static boolean canAssignFrom(RelDataType type1, RelDataType type2,
+      RelDataTypeFactory typeFactory) {
     final SqlTypeName name1 = type1.getSqlTypeName();
     final SqlTypeName name2 = type2.getSqlTypeName();
     if (name1.getFamily() == name2.getFamily()) {
       switch (name1.getFamily()) {
       case NUMERIC:
-        return name1.compareTo(name2) >= 0;
+        if (SqlTypeUtil.isExactNumeric(type1)
+            && SqlTypeUtil.isExactNumeric(type2)) {
+          int precision1;
+          int scale1;
+          if (name1 == SqlTypeName.DECIMAL) {
+            type1 = typeFactory.decimalOf(type1);
+            precision1 = type1.getPrecision();
+            scale1 = type1.getScale();
+          } else {
+            precision1 = typeFactory.getTypeSystem().getMaxPrecision(name1);
+            scale1 = typeFactory.getTypeSystem().getMaxScale(name1);
+          }
+          int precision2;
+          int scale2;
+          if (name2 == SqlTypeName.DECIMAL) {
+            type2 = typeFactory.decimalOf(type2);
+            precision2 = type2.getPrecision();
+            scale2 = type2.getScale();
+          } else {
+            precision2 = typeFactory.getTypeSystem().getMaxPrecision(name2);
+            scale2 = typeFactory.getTypeSystem().getMaxScale(name2);
+          }
+          return precision1 >= precision2
+              && scale1 >= scale2;
+        } else if (SqlTypeUtil.isApproximateNumeric(type1)
+            && SqlTypeUtil.isApproximateNumeric(type2)) {
+          return type1.getPrecision() >= type2.getPrecision()
+              && type1.getScale() >= type2.getScale();
+        }
+        break;
       default:
-        return true;
+        // getPrecision() will return:
+        // - number of decimal digits for fractional seconds for datetime types
+        // - length in characters for character types
+        // - length in bytes for binary types
+        // - RelDataType.PRECISION_NOT_SPECIFIED (-1) if not applicable for this type
+        return type1.getPrecision() >= type2.getPrecision();
       }
     }
     return false;
diff --git a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
index 390bfa4..53676e4 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
@@ -2196,12 +2196,17 @@ class RexProgramTest extends RexProgramTestBase {
 
   @Test void testConstantMap() {
     final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
+    final RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT);
+    final RelDataType decimalType = typeFactory.createSqlType(SqlTypeName.DECIMAL, 4, 2);
+    final RelDataType charType = typeFactory.createSqlType(SqlTypeName.CHAR, 5);
     final RelDataType rowType = typeFactory.builder()
         .add("a", intType)
         .add("b", intType)
         .add("c", intType)
         .add("d", intType)
-        .add("e", intType)
+        .add("e", bigintType)
+        .add("f", decimalType)
+        .add("g", charType)
         .build();
 
     final RexDynamicParam range = rexBuilder.makeDynamicParam(rowType, 0);
@@ -2210,6 +2215,8 @@ class RexProgramTest extends RexProgramTestBase {
     final RexNode cRef = rexBuilder.makeFieldAccess(range, 2);
     final RexNode dRef = rexBuilder.makeFieldAccess(range, 3);
     final RexNode eRef = rexBuilder.makeFieldAccess(range, 4);
+    final RexNode fRef = rexBuilder.makeFieldAccess(range, 5);
+    final RexNode gRef = rexBuilder.makeFieldAccess(range, 6);
     final RexLiteral literal1 = rexBuilder.makeExactLiteral(BigDecimal.ONE);
     final RexLiteral literal2 = rexBuilder.makeExactLiteral(BigDecimal.valueOf(2));
 
@@ -2237,6 +2244,41 @@ class RexProgramTest extends RexProgramTestBase {
             ImmutableList.of(eq(aRef, literal1),
                 eq(aRef, literal2)));
     assertThat(getString(map3), is("{1=?0.a, 2=?0.a}"));
+
+    // Different precision and scale in decimal
+    final ImmutableMap<RexNode, RexNode> map4 =
+        RexUtil.predicateConstants(RexNode.class, rexBuilder,
+            ImmutableList.of(
+                eq(cast(fRef, typeFactory.createSqlType(SqlTypeName.DECIMAL, 3, 1)),
+                    rexBuilder.makeExactLiteral(BigDecimal.valueOf(21.2)))));
+    assertThat(
+        getString(map4), is("{21.2:DECIMAL(3, 1)=CAST(?0.f):DECIMAL(3, 1) NOT NULL,"
+        + " CAST(?0.f):DECIMAL(3, 1) NOT NULL=21.2:DECIMAL(3, 1)}"));
+
+    // Different precision in char
+    final ImmutableMap<RexNode, RexNode> map5 =
+        RexUtil.predicateConstants(RexNode.class, rexBuilder,
+            ImmutableList.of(
+                eq(cast(gRef, typeFactory.createSqlType(SqlTypeName.CHAR, 3)),
+                    rexBuilder.makeLiteral("abc"))));
+    assertThat(
+        getString(map5), is("{'abc'=CAST(?0.g):CHAR(3) NOT NULL,"
+        + " CAST(?0.g):CHAR(3) NOT NULL='abc'}"));
+
+    // Cast bigint to int
+    final ImmutableMap<RexNode, RexNode> map6 =
+        RexUtil.predicateConstants(RexNode.class, rexBuilder,
+            ImmutableList.of(
+                eq(cast(eRef, typeFactory.createSqlType(SqlTypeName.INTEGER)), literal1)));
+    assertThat(
+        getString(map6), is("{1=CAST(?0.e):INTEGER NOT NULL, CAST(?0.e):INTEGER NOT NULL=1}"));
+
+    // Cast int to bigint
+    final ImmutableMap<RexNode, RexNode> map7 =
+        RexUtil.predicateConstants(RexNode.class, rexBuilder,
+            ImmutableList.of(
+                eq(cast(aRef, typeFactory.createSqlType(SqlTypeName.BIGINT)), literal1)));
+    assertThat(getString(map7), is("{1=CAST(?0.a):BIGINT NOT NULL, ?0.a=1}"));
   }
 
   @Test void notDistinct() {