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))"));
+ }
}