From 601ad8e6d306223c8bc713cd1299165d05b473e1 Mon Sep 17 00:00:00 2001 From: xuzifu666 <1206332514@qq.com> Date: Sun, 24 May 2026 20:58:09 +0800 Subject: [PATCH] [CALCITE-7304] Floor/Ceil can not simplify with WEEK TimeUnit --- .../org/apache/calcite/rex/RexSimplify.java | 14 ++++ .../test/RexImplicationCheckerTest.java | 79 +++++++++++++++++++ .../calcite/test/SqlToRelConverterTest.java | 20 +++++ 3 files changed, 113 insertions(+) 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 1c38d2d72147..4d7702295bff 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java @@ -2687,6 +2687,20 @@ private static boolean canRollUp(TimeUnit outer, TimeUnit inner) { break; } break; + case WEEK: + switch (inner) { + case WEEK: + case DAY: + case HOUR: + case MINUTE: + case SECOND: + case MILLISECOND: + case MICROSECOND: + return true; + default: + break; + } + break; case QUARTER: switch (inner) { case QUARTER: 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 042dcae1f0a9..60fbc4e6b821 100644 --- a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java @@ -24,6 +24,7 @@ import org.apache.calcite.rex.RexSimplify; import org.apache.calcite.rex.RexUnknownAs; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.DateString; @@ -556,4 +557,82 @@ public class RexImplicationCheckerTest { } } + /** Test case of + * [CALCITE-7304] + * Floor/Ceil can not simplify with WEEK TimeUnit. */ + @Test void testSimplifyCeilFloorWeek() { + final Fixture f = new Fixture(); + // RexInterpreter does not support WEEK and DAY, so we disable paranoid + // verification for this test. + final RexSimplify nonParanoidSimplify = f.simplify.withParanoid(false); + final RexNode literalTs = + f.timestampLiteral(new TimestampString("2010-10-10 00:00:00")); + + // Positive tests: floor(floor(x, inner), WEEK) -> floor(x, WEEK) + // when inner is DAY or finer. + for (TimeUnitRange innerRange : ImmutableList.of( + TimeUnitRange.WEEK, TimeUnitRange.DAY)) { + final RexNode innerFloorCall = + f.rexBuilder.makeCall(SqlStdOperatorTable.FLOOR, literalTs, + f.rexBuilder.makeFlag(innerRange)); + final RexNode innerCeilCall = + f.rexBuilder.makeCall(SqlStdOperatorTable.CEIL, literalTs, + f.rexBuilder.makeFlag(innerRange)); + final RexNode outerFloorCall = + f.rexBuilder.makeCall(SqlStdOperatorTable.FLOOR, innerFloorCall, + f.rexBuilder.makeFlag(TimeUnitRange.WEEK)); + final RexNode outerCeilCall = + f.rexBuilder.makeCall(SqlStdOperatorTable.CEIL, innerCeilCall, + f.rexBuilder.makeFlag(TimeUnitRange.WEEK)); + final RexCall floorSimplifiedExpr = + (RexCall) nonParanoidSimplify.simplifyPreservingType(outerFloorCall, + RexUnknownAs.UNKNOWN, true); + assertThat(floorSimplifiedExpr.getKind(), is(SqlKind.FLOOR)); + assertThat(((RexLiteral) floorSimplifiedExpr.getOperands().get(1)) + .getValue(), + hasToString(TimeUnitRange.WEEK.toString())); + assertThat(floorSimplifiedExpr.getOperands().get(0), + hasToString(literalTs.toString())); + final RexCall ceilSimplifiedExpr = + (RexCall) nonParanoidSimplify.simplifyPreservingType(outerCeilCall, + RexUnknownAs.UNKNOWN, true); + assertThat(ceilSimplifiedExpr.getKind(), is(SqlKind.CEIL)); + assertThat(((RexLiteral) ceilSimplifiedExpr.getOperands().get(1)) + .getValue(), + hasToString(TimeUnitRange.WEEK.toString())); + assertThat(ceilSimplifiedExpr.getOperands().get(0), + hasToString(literalTs.toString())); + } + + // Negative tests: WEEK cannot rollup to MONTH or DAY, + // and MONTH cannot rollup to WEEK. + for (TimeUnitRange outerRange : ImmutableList.of( + TimeUnitRange.MONTH, TimeUnitRange.DAY)) { + assertNotSimplified(f, nonParanoidSimplify, SqlStdOperatorTable.FLOOR, literalTs, + TimeUnitRange.WEEK, outerRange); + assertNotSimplified(f, nonParanoidSimplify, SqlStdOperatorTable.CEIL, literalTs, + TimeUnitRange.WEEK, outerRange); + } + for (TimeUnitRange innerRange : ImmutableList.of( + TimeUnitRange.MONTH, TimeUnitRange.YEAR)) { + assertNotSimplified(f, nonParanoidSimplify, SqlStdOperatorTable.FLOOR, literalTs, + innerRange, TimeUnitRange.WEEK); + assertNotSimplified(f, nonParanoidSimplify, SqlStdOperatorTable.CEIL, literalTs, + innerRange, TimeUnitRange.WEEK); + } + } + + private void assertNotSimplified(Fixture f, RexSimplify simplify, SqlOperator operator, + RexNode timestamp, TimeUnitRange innerRange, TimeUnitRange outerRange) { + final RexNode innerCall = + f.rexBuilder.makeCall(operator, timestamp, + f.rexBuilder.makeFlag(innerRange)); + final RexNode outerCall = + f.rexBuilder.makeCall(operator, innerCall, + f.rexBuilder.makeFlag(outerRange)); + final RexNode simplifiedExpr = + simplify.simplifyPreservingType(outerCall, RexUnknownAs.UNKNOWN, true); + assertThat(simplifiedExpr, hasToString(outerCall.toString())); + } + } diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java index ebad0e9450a5..9217e74895ea 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java @@ -76,9 +76,11 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasSize; /** @@ -6133,4 +6135,22 @@ void checkUserDefinedOrderByOver(NullCollation nullCollation) { + "FROM emp JOIN dept using (deptno)"; sql(sql).withConformance(SqlConformanceEnum.PRESTO).ok(); } + + /** Test case of + * [CALCITE-7304] + * Floor/Ceil can not simplify with WEEK TimeUnit. */ + @Test void testSimplifyNestedFloorWeekFromSql() { + final String sql = "select floor(floor(hiredate TO DAY) TO WEEK) from emp"; + final RelNode rel = sql(sql).toRel(); + final HepProgramBuilder programBuilder = HepProgram.builder(); + programBuilder.addRuleInstance(CoreRules.PROJECT_REDUCE_EXPRESSIONS); + final HepPlanner planner = new HepPlanner(programBuilder.build()); + planner.setRoot(rel); + final RelNode optimized = planner.findBestExp(); + final String plan = RelOptUtil.toString(optimized); + // After PROJECT_REDUCE_EXPRESSIONS, nested floor(floor(x TO DAY) TO WEEK) + // should be simplified to floor(x TO WEEK). + assertThat(plan, not(containsString("FLOOR(FLOOR"))); + assertThat(plan, containsString("FLOOR($4, FLAG(WEEK))")); + } }