Repository: calcite Updated Branches: refs/heads/master cc8398742 -> 495fd1dc7
[CALCITE-1738] Push CAST of literals to Druid (Remus Rusanu) Close apache/calcite#430 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/495fd1dc Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/495fd1dc Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/495fd1dc Branch: refs/heads/master Commit: 495fd1dc7b0c1805834964a98fc042e12c76d7a7 Parents: cc83987 Author: Remus Rusanu <[email protected]> Authored: Mon Apr 17 05:05:59 2017 -0700 Committer: Julian Hyde <[email protected]> Committed: Thu Apr 20 10:19:38 2017 -0700 ---------------------------------------------------------------------- .../calcite/test/RexImplicationCheckerTest.java | 4 ++ .../adapter/druid/DruidDateTimeUtils.java | 24 ++++++++++- .../calcite/test/DruidDateRangeRulesTest.java | 45 ++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/495fd1dc/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 f321551..c027164 100644 --- a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java @@ -501,6 +501,10 @@ public class RexImplicationCheckerTest { calendar, timeDataType.getPrecision()); } + public RexNode cast(RelDataType type, RexNode exp) { + return rexBuilder.makeCast(type, exp, true); + } + void checkImplies(RexNode node1, RexNode node2) { final String message = node1 + " does not imply " + node2 + " when it should"; http://git-wip-us.apache.org/repos/asf/calcite/blob/495fd1dc/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java index 1312783..e3e86c3 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java @@ -23,6 +23,7 @@ import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Util; import org.apache.calcite.util.trace.CalciteTrace; @@ -243,12 +244,31 @@ public class DruidDateTimeUtils { } private static Calendar literalValue(RexNode node) { - if (node instanceof RexLiteral) { + switch (node.getKind()) { + case LITERAL: + assert node instanceof RexLiteral; Object value = ((RexLiteral) node).getValue(); if (value instanceof Calendar) { return (Calendar) value; } - return null; + break; + case CAST: + // Normally all CASTs are eliminated by now by constant reduction. + // But when HiveExecutor is used there may be a cast that changes only + // nullability, from TIMESTAMP NOT NULL literal to TIMESTAMP literal. + // We can handle that case by traversing the dummy CAST. + assert node instanceof RexCall; + final RexCall call = (RexCall) node; + final RexNode operand = call.getOperands().get(0); + final RelDataType callType = call.getType(); + final RelDataType operandType = operand.getType(); + if (operand.getKind() == SqlKind.LITERAL + && callType.getSqlTypeName() == SqlTypeName.TIMESTAMP + && callType.isNullable() + && operandType.getSqlTypeName() == SqlTypeName.TIMESTAMP + && !operandType.isNullable()) { + return literalValue(operand); + } } return null; } http://git-wip-us.apache.org/repos/asf/calcite/blob/495fd1dc/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java ---------------------------------------------------------------------- diff --git a/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java b/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java index 8273130..b2cf321 100644 --- a/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java +++ b/druid/src/test/java/org/apache/calcite/test/DruidDateRangeRulesTest.java @@ -23,6 +23,7 @@ import org.apache.calcite.rel.rules.DateRangeRules; import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.test.RexImplicationCheckerTest.Fixture; +import org.apache.calcite.util.Util; import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; @@ -37,6 +38,7 @@ import java.util.List; import java.util.Map; import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.Assert.assertThat; /** Unit tests for {@link DateRangeRules} algorithms. */ @@ -116,6 +118,49 @@ public class DruidDateRangeRulesTest { + "2016-02-29T00:00:00.000/2016-03-01T00:00:00.000]")); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-1738">[CALCITE-1738] + * Push CAST of literals to Druid</a>. */ + @Test public void testFilterWithCast() { + final Fixture2 f = new Fixture2(); + Calendar from = Util.calendar(); + from.clear(); + from.set(2010, Calendar.JANUARY, 1); + Calendar to = Util.calendar(); + to.clear(); + to.set(2011, Calendar.JANUARY, 1); + + // dt >= 2010-01-01 AND dt < 2011-01-01 + checkDateRangeNoSimplify(f, + f.and( + f.ge(f.dt, f.cast(f.timeStampDataType, f.timestampLiteral(from))), + f.lt(f.dt, f.cast(f.timeStampDataType, f.timestampLiteral(to)))), + is("[2010-01-01T00:00:00.000/2011-01-01T00:00:00.000]")); + } + + // For testFilterWithCast we need to no simplify the expression, which would + // remove the CAST, in order to match the way expressions are presented when + // HiveRexExecutorImpl is used in Hive + private void checkDateRangeNoSimplify(Fixture f, RexNode e, + Matcher<String> intervalMatcher) { + final Map<String, RangeSet<Calendar>> operandRanges = new HashMap<>(); + // We rely on the collection being sorted (so YEAR comes before MONTH + // before HOUR) and unique. A predicate on MONTH is not useful if there is + // no predicate on YEAR. Then when we apply the predicate on DAY it doesn't + // generate hundreds of ranges we'll later throw away. + final List<TimeUnitRange> timeUnits = + Ordering.natural().sortedCopy(DateRangeRules.extractTimeUnits(e)); + for (TimeUnitRange timeUnit : timeUnits) { + e = e.accept( + new DateRangeRules.ExtractShuttle(f.rexBuilder, timeUnit, + operandRanges)); + } + final List<LocalInterval> intervals = + DruidDateTimeUtils.createInterval(f.timeStampDataType, e); + assertThat(intervals, notNullValue()); + assertThat(intervals.toString(), intervalMatcher); + } + private void checkDateRange(Fixture f, RexNode e, Matcher<String> intervalMatcher) { final Map<String, RangeSet<Calendar>> operandRanges = new HashMap<>(); // We rely on the collection being sorted (so YEAR comes before MONTH
