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 2018/02/15 23:48:33 UTC

calcite git commit: [CALCITE-2178] Extend expression simplifier to work on datetime CEIL/FLOOR functions

Repository: calcite
Updated Branches:
  refs/heads/master 6981778c5 -> 0ced3b7f5


[CALCITE-2178] Extend expression simplifier to work on datetime CEIL/FLOOR functions

Close apache/calcite#628


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/0ced3b7f
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/0ced3b7f
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/0ced3b7f

Branch: refs/heads/master
Commit: 0ced3b7f5d641c034a7c8867ec088e5c2decf4e3
Parents: 6981778
Author: Jesus Camacho Rodriguez <jc...@apache.org>
Authored: Wed Feb 14 14:37:22 2018 -0800
Committer: Jesus Camacho Rodriguez <jc...@apache.org>
Committed: Thu Feb 15 15:48:24 2018 -0800

----------------------------------------------------------------------
 .../org/apache/calcite/rex/RexSimplify.java     | 94 ++++++++++++++++++++
 .../calcite/test/RexImplicationCheckerTest.java | 49 ++++++++++
 2 files changed, 143 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/0ced3b7f/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index e1b0258..568ddbc 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.rex;
 
+import org.apache.calcite.avatica.util.TimeUnit;
+import org.apache.calcite.avatica.util.TimeUnitRange;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptUtil;
@@ -142,6 +144,9 @@ public class RexSimplify {
       return simplifyCase((RexCall) e);
     case CAST:
       return simplifyCast((RexCall) e);
+    case CEIL:
+    case FLOOR:
+      return simplifyCeilFloor((RexCall) e);
     case IS_NULL:
     case IS_NOT_NULL:
     case IS_TRUE:
@@ -949,6 +954,95 @@ public class RexSimplify {
     }
   }
 
+  /** Tries to simplify CEIL/FLOOR function on top of CEIL/FLOOR.
+   *
+   * <p>Examples:
+   * <ul>
+   *
+   * <li>{@code ceil(floor($0, flag(hour)), flag(day))} returns {@code ceil($0, flag(day))}
+   *
+   * <li>{@code floor(floor($0, flag(second)), flag(day))} returns {@code floor($0, flag(day))}
+   *
+   * <li>{@code floor(ceil($0, flag(day)), flag(second))} does not change
+   *
+   * </ul>
+   */
+  private RexNode simplifyCeilFloor(RexCall e) {
+    if (e.getOperands().size() != 2) {
+      // Bail out since we only simplify ceil/floor <date>
+      return e;
+    }
+    final RexNode operand = simplify(e.getOperands().get(0));
+    switch (operand.getKind()) {
+    case CEIL:
+    case FLOOR:
+      // CEIL/FLOOR on top of CEIL/FLOOR
+      final RexCall child = (RexCall) operand;
+      if (child.getOperands().size() != 2) {
+        // Bail out since we only simplify ceil/floor <date>
+        return e;
+      }
+      final RexLiteral parentFlag = (RexLiteral) e.operands.get(1);
+      final TimeUnitRange parentFlagValue = (TimeUnitRange) parentFlag.getValue();
+      final RexLiteral childFlag = (RexLiteral) child.operands.get(1);
+      final TimeUnitRange childFlagValue = (TimeUnitRange) childFlag.getValue();
+      if (parentFlagValue != null && childFlagValue != null) {
+        if (canRollUp(parentFlagValue.startUnit, childFlagValue.startUnit)) {
+          return e.clone(e.getType(),
+              ImmutableList.of(child.getOperands().get(0), parentFlag));
+        }
+      }
+    }
+    return e.clone(e.getType(),
+        ImmutableList.of(operand, e.getOperands().get(1)));
+  }
+
+  /** Method that returns whether we can rollup from inner time unit
+   * to outer time unit. */
+  private static boolean canRollUp(TimeUnit outer, TimeUnit inner) {
+    // Special handling for QUARTER as it is not in the expected
+    // order in TimeUnit
+    switch (outer) {
+    case YEAR:
+    case MONTH:
+    case DAY:
+    case HOUR:
+    case MINUTE:
+    case SECOND:
+    case MILLISECOND:
+    case MICROSECOND:
+      switch (inner) {
+      case YEAR:
+      case QUARTER:
+      case MONTH:
+      case DAY:
+      case HOUR:
+      case MINUTE:
+      case SECOND:
+      case MILLISECOND:
+      case MICROSECOND:
+        if (inner == TimeUnit.QUARTER) {
+          return outer == TimeUnit.YEAR || outer == TimeUnit.QUARTER;
+        }
+        return outer.ordinal() <= inner.ordinal();
+      }
+      break;
+    case QUARTER:
+      switch (inner) {
+      case QUARTER:
+      case MONTH:
+      case DAY:
+      case HOUR:
+      case MINUTE:
+      case SECOND:
+      case MILLISECOND:
+      case MICROSECOND:
+        return true;
+      }
+    }
+    return false;
+  }
+
   /** Removes any casts that change nullability but not type.
    *
    * <p>For example, {@code CAST(1 = 0 AS BOOLEAN)} becomes {@code 1 = 0}. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/0ced3b7f/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
index 296b497..9520e45 100644
--- a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.test;
 
 import org.apache.calcite.DataContext;
+import org.apache.calcite.avatica.util.TimeUnitRange;
 import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptPredicateList;
@@ -26,6 +27,7 @@ import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexExecutorImpl;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
@@ -36,6 +38,7 @@ import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.schema.Schemas;
 import org.apache.calcite.server.CalciteServerStatement;
 import org.apache.calcite.sql.SqlCollation;
+import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.tools.Frameworks;
 import org.apache.calcite.util.DateString;
@@ -45,6 +48,8 @@ import org.apache.calcite.util.TimeString;
 import org.apache.calcite.util.TimestampString;
 import org.apache.calcite.util.Util;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Test;
 
 import java.math.BigDecimal;
@@ -371,6 +376,50 @@ public class RexImplicationCheckerTest {
         is("2014"));
   }
 
+  /** Test case for simplifier of ceil and floor. */
+  @Test public void testSimplifyFloor() {
+    final ImmutableList<TimeUnitRange> timeUnitRanges =
+        ImmutableList.of(TimeUnitRange.WEEK,
+            TimeUnitRange.YEAR, TimeUnitRange.QUARTER, TimeUnitRange.MONTH, TimeUnitRange.DAY,
+            TimeUnitRange.HOUR, TimeUnitRange.MINUTE, TimeUnitRange.SECOND,
+            TimeUnitRange.MILLISECOND, TimeUnitRange.MICROSECOND);
+    final Fixture f = new Fixture();
+    final RexUtil.ExprSimplifier defaultSimplifier =
+        new RexUtil.ExprSimplifier(f.simplify, true);
+
+    final RexNode literalTs =
+        f.timestampLiteral(new TimestampString("2010-10-10 00:00:00"));
+    // Exclude WEEK as it is only used for the negative tests
+    for (int i = 1; i < timeUnitRanges.size(); i++) {
+      final RexNode innerFloorCall = f.rexBuilder.makeCall(
+          SqlStdOperatorTable.CEIL, literalTs,
+          f.rexBuilder.makeFlag(timeUnitRanges.get(i)));
+      for (int j = 1; j <= i; j++) {
+        final RexNode outerFloorCall = f.rexBuilder.makeCall(
+            SqlStdOperatorTable.FLOOR, innerFloorCall,
+            f.rexBuilder.makeFlag(timeUnitRanges.get(j)));
+        final RexCall simplifiedExpr = (RexCall) defaultSimplifier.apply(outerFloorCall);
+        assertThat(simplifiedExpr.getKind(), is(SqlKind.FLOOR));
+        assertThat(((RexLiteral) simplifiedExpr.getOperands().get(1)).getValue().toString(),
+            is(timeUnitRanges.get(j).toString()));
+        assertThat(simplifiedExpr.getOperands().get(0).toString(), is(literalTs.toString()));
+      }
+    }
+    // Negative test
+    for (int i = timeUnitRanges.size() - 1; i >= 0; i--) {
+      final RexNode innerFloorCall = f.rexBuilder.makeCall(
+          SqlStdOperatorTable.FLOOR, literalTs,
+          f.rexBuilder.makeFlag(timeUnitRanges.get(i)));
+      for (int j = timeUnitRanges.size() - 1; j > i; j--) {
+        final RexNode outerFloorCall = f.rexBuilder.makeCall(
+            SqlStdOperatorTable.CEIL, innerFloorCall,
+            f.rexBuilder.makeFlag(timeUnitRanges.get(j)));
+        final RexCall simplifiedExpr = (RexCall) defaultSimplifier.apply(outerFloorCall);
+        assertThat(simplifiedExpr.toString(), is(outerFloorCall.toString()));
+      }
+    }
+  }
+
   /** Contains all the nourishment a test case could possibly need.
    *
    * <p>We put the data in here, rather than as fields in the test case, so that