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