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: + *

+ * + *

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")