diff --git a/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java b/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
index 1b48b8c64f82..388a0748837b 100644
--- a/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
+++ b/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
@@ -28,8 +28,11 @@
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.util.DateString;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.TimeString;
+import org.apache.calcite.util.TimestampString;
import org.apache.calcite.util.trace.CalciteLogger;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -38,6 +41,8 @@
import java.math.BigDecimal;
import java.util.List;
+import static java.util.Objects.requireNonNull;
+
/**
* DataContext for evaluating a RexExpression.
*/
@@ -107,6 +112,26 @@ public VisitorDataContext(@Nullable Object[] values) {
return new VisitorDataContext(values);
}
+ /**
+ * Extracts a value from a RexLiteral for use in DataContext.
+ *
+ *
Returns a Pair of (column index, value) if extraction is successful,
+ * or null if the value cannot be extracted or is invalid.
+ *
+ *
Returns null when:
+ *
+ * - Arguments are not valid RexInputRef and RexLiteral
+ * - Type conversion fails (e.g., invalid date/time format)
+ * - Type combination is unsupported
+ *
+ *
+ * When null is returned, the containing optimization (e.g.,
+ * materialized view substitution) cannot be applied and is skipped.
+ *
+ * @param inputRef the input reference (column)
+ * @param literal the literal value to extract
+ * @return a Pair of (column index, value) or null
+ */
public static @Nullable Pair getValue(
@Nullable RexNode inputRef, @Nullable RexNode literal) {
inputRef = inputRef == null ? null : RexUtil.removeCast(inputRef);
@@ -140,10 +165,68 @@ public VisitorDataContext(@Nullable Object[] values) {
case DECIMAL:
return Pair.of(index, rexLiteral.getValueAs(BigDecimal.class));
case DATE:
+ switch (rexLiteral.getType().getSqlTypeName()) {
+ case DATE:
+ return Pair.of(index, rexLiteral.getValueAs(Integer.class));
+ case CHAR:
+ case VARCHAR:
+ try {
+ return Pair.of(index,
+ new DateString(requireNonNull(rexLiteral.getValueAs(String.class)))
+ .getDaysSinceEpoch());
+ } catch (IllegalArgumentException e) {
+ LOGGER.warn(
+ "Cannot convert string literal '{}' to DATE type; "
+ + "materialized view optimization will be skipped",
+ rexLiteral.getValueAs(String.class), e);
+ return null;
+ }
+ default:
+ break;
+ }
+ break;
case TIME:
- return Pair.of(index, rexLiteral.getValueAs(Integer.class));
+ switch (rexLiteral.getType().getSqlTypeName()) {
+ case TIME:
+ return Pair.of(index, rexLiteral.getValueAs(Integer.class));
+ case CHAR:
+ case VARCHAR:
+ try {
+ return Pair.of(index,
+ new TimeString(requireNonNull(rexLiteral.getValueAs(String.class)))
+ .getMillisOfDay());
+ } catch (IllegalArgumentException e) {
+ LOGGER.debug(
+ "Cannot convert string literal '{}' to TIME type; "
+ + "materialized view optimization will be skipped",
+ rexLiteral.getValueAs(String.class), e);
+ return null;
+ }
+ default:
+ break;
+ }
+ break;
case TIMESTAMP:
- return Pair.of(index, rexLiteral.getValueAs(Long.class));
+ switch (rexLiteral.getType().getSqlTypeName()) {
+ case TIMESTAMP:
+ return Pair.of(index, rexLiteral.getValueAs(Long.class));
+ case CHAR:
+ case VARCHAR:
+ try {
+ return Pair.of(index,
+ new TimestampString(requireNonNull(rexLiteral.getValueAs(String.class)))
+ .getMillisSinceEpoch());
+ } catch (IllegalArgumentException e) {
+ LOGGER.debug(
+ "Cannot convert string literal '{}' to TIMESTAMP type; "
+ + "materialized view optimization will be skipped",
+ rexLiteral.getValueAs(String.class), e);
+ return null;
+ }
+ default:
+ break;
+ }
+ break;
case CHAR:
return Pair.of(index, rexLiteral.getValueAs(Character.class));
case VARCHAR:
diff --git a/core/src/test/java/org/apache/calcite/test/MaterializedViewSubstitutionVisitorTest.java b/core/src/test/java/org/apache/calcite/test/MaterializedViewSubstitutionVisitorTest.java
index 22fa948ed667..a19cd683d21b 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializedViewSubstitutionVisitorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializedViewSubstitutionVisitorTest.java
@@ -112,6 +112,21 @@ protected final MaterializedViewFixture sql(String materialize,
.ok();
}
+ /** Test case of
+ * [CALCITE-6823]
+ * Cannot convert CHAR to Integer when applying SubstitutionVisitor. */
+ @Test void testDateFilter() {
+ sql("SELECT HIREDATE FROM EMP WHERE HIREDATE > '1990-10-01'",
+ "SELECT * FROM EMP WHERE HIREDATE > '1990-05-01'")
+ .withDefaultSchemaSpec(CalciteAssert.SchemaSpec.SCOTT)
+ .noMat();
+
+ sql("SELECT HIREDATE FROM EMP WHERE HIREDATE > '1990-10-01'",
+ "SELECT * FROM EMP WHERE HIREDATE > 'invalid-date'")
+ .withDefaultSchemaSpec(CalciteAssert.SchemaSpec.SCOTT)
+ .noMat();
+ }
+
@Test void testFilterToProject0() {
sql("select *, \"empid\" * 2 from \"emps\"",
"select * from \"emps\" where (\"empid\" * 2) > 3")