This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/calcite.git
commit c4155b8daba4d2d5048a5877df82a57aede1ddc7 Author: Tanner Clary <[email protected]> AuthorDate: Tue Jan 24 23:28:58 2023 +0000 [CALCITE-5489] When creating a RexCall to TIMESTAMP_DIFF function, cannot convert a TIMESTAMP literal to a org.apache.calcite.avatica.util.TimeUnit Close apache/calcite#3040 Close apache/calcite#3048 Co-authored-by: Tanner Clary <[email protected]> Co-authored-by: Sergey Nuyanzin <[email protected]> --- babel/src/test/resources/sql/big-query.iq | 11 ++++++++ .../org/apache/calcite/sql/SqlOperatorBinding.java | 13 +++++++++ .../calcite/sql/fun/SqlTimestampDiffFunction.java | 10 +++---- .../org/apache/calcite/sql/type/SqlTypeName.java | 6 ++++ .../org/apache/calcite/rex/RexBuilderTest.java | 33 ++++++++++++++++++++++ core/src/test/resources/sql/misc.iq | 11 ++++++++ 6 files changed, 79 insertions(+), 5 deletions(-) diff --git a/babel/src/test/resources/sql/big-query.iq b/babel/src/test/resources/sql/big-query.iq index 4b1c4bc501..20107afe66 100755 --- a/babel/src/test/resources/sql/big-query.iq +++ b/babel/src/test/resources/sql/big-query.iq @@ -1791,6 +1791,17 @@ SELECT TIMESTAMP_DIFF("2001-02-01 01:00:00", "2001-02-01 00:00:01", HOUR) AS neg +---------------+ (1 row) +!ok + +# In this example, the time unit is a 'flag' literal. +SELECT TIMESTAMP_DIFF(TIMESTAMP '2008-12-25', TIMESTAMP '2008-09-25', `quarter`) as diff; ++------+ +| diff | ++------+ +| 1 | ++------+ +(1 row) + !ok ##################################################################### # DATE_TRUNC diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java index 6a08a56d1d..04e64cfeca 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java @@ -22,6 +22,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelDataTypeFactoryImpl; import org.apache.calcite.runtime.CalciteException; import org.apache.calcite.runtime.Resources; +import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.validate.SqlMonotonicity; import org.apache.calcite.sql.validate.SqlValidatorException; import org.apache.calcite.util.NlsString; @@ -195,6 +196,18 @@ public abstract class SqlOperatorBinding { throw new UnsupportedOperationException(); } + /** + * Returns whether an operand is a time frame. + * + * @param ordinal zero-based ordinal of operand of interest + * @return whether operand is a time frame + */ + public boolean isOperandTimeFrame(int ordinal) { + return getOperandCount() > 0 + && SqlTypeName.TIME_FRAME_TYPES.contains( + getOperandType(ordinal).getSqlTypeName()); + } + /** Returns the number of bound operands. */ public abstract int getOperandCount(); diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampDiffFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampDiffFunction.java index 2955e778f3..44b077da0d 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampDiffFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampDiffFunction.java @@ -63,14 +63,14 @@ class SqlTimestampDiffFunction extends SqlFunction { final TimeUnit timeUnit; final RelDataType type1; final RelDataType type2; - if (opBinding.isOperandLiteral(0, true)) { - type1 = opBinding.getOperandType(0); - type2 = opBinding.getOperandType(1); - timeUnit = opBinding.getOperandLiteralValue(2, TimeUnit.class); - } else { + if (opBinding.isOperandTimeFrame(0)) { timeUnit = opBinding.getOperandLiteralValue(0, TimeUnit.class); type1 = opBinding.getOperandType(1); type2 = opBinding.getOperandType(2); + } else { + type1 = opBinding.getOperandType(0); + type2 = opBinding.getOperandType(1); + timeUnit = opBinding.getOperandLiteralValue(2, TimeUnit.class); } SqlTypeName sqlTypeName = timeUnit == TimeUnit.NANOSECOND diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java index 526cad95de..c2939c65d1 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java @@ -216,6 +216,12 @@ public enum SqlTypeName { Sets.immutableEnumSet( Iterables.concat(YEAR_INTERVAL_TYPES, DAY_INTERVAL_TYPES)); + /** The possible types of a time frame argument to a function such as + * {@code TIMESTAMP_DIFF}. */ + public static final Set<SqlTypeName> TIME_FRAME_TYPES = + Sets.immutableEnumSet( + Iterables.concat(INTERVAL_TYPES, ImmutableList.of(SYMBOL))); + private static final Map<Integer, SqlTypeName> JDBC_TYPE_TO_NAME = ImmutableMap.<Integer, SqlTypeName>builder() .put(Types.TINYINT, TINYINT) diff --git a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java index 916237630b..a4e7395a4e 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java @@ -17,6 +17,7 @@ package org.apache.calcite.rex; import org.apache.calcite.avatica.util.ByteString; +import org.apache.calcite.avatica.util.TimeUnit; import org.apache.calcite.rel.core.CorrelationId; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; @@ -27,10 +28,12 @@ import org.apache.calcite.rel.type.RelDataTypeSystem; import org.apache.calcite.rel.type.RelDataTypeSystemImpl; import org.apache.calcite.sql.SqlCollation; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.fun.SqlLibraryOperators; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.BasicSqlType; import org.apache.calcite.sql.type.SqlTypeFactoryImpl; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.test.RexImplicationCheckerFixtures; import org.apache.calcite.util.DateString; import org.apache.calcite.util.Litmus; import org.apache.calcite.util.NlsString; @@ -850,4 +853,34 @@ class RexBuilderTest { // and the suffix should not be removed. assertThat(literal.digest, is("0L:(udt)NOT NULL")); } + + + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-5489">[CALCITE-5489] + * RexCall to TIMESTAMP_DIFF function fails to convert a TIMESTAMP literal to + * a org.apache.calcite.avatica.util.TimeUnit</a>. */ + @Test void testTimestampDiffCall() { + final RexImplicationCheckerFixtures.Fixture f = + new RexImplicationCheckerFixtures.Fixture(); + final TimestampString ts = + TimestampString.fromCalendarFields(Util.calendar()); + final RexNode literal = f.timestampLiteral(ts); + final RexLiteral flag = f.rexBuilder.makeFlag(TimeUnit.QUARTER); + assertThat( + f.rexBuilder.makeCall(SqlLibraryOperators.DATEDIFF, + flag, literal, literal), + notNullValue()); + assertThat( + f.rexBuilder.makeCall(SqlStdOperatorTable.TIMESTAMP_DIFF, + flag, literal, literal), + notNullValue()); + assertThat( + f.rexBuilder.makeCall(SqlLibraryOperators.TIMESTAMP_DIFF3, + literal, literal, flag), + notNullValue()); + assertThat( + f.rexBuilder.makeCall(SqlLibraryOperators.TIME_DIFF, + literal, literal, flag), + notNullValue()); + } } diff --git a/core/src/test/resources/sql/misc.iq b/core/src/test/resources/sql/misc.iq index 8ce073905f..38e0069370 100644 --- a/core/src/test/resources/sql/misc.iq +++ b/core/src/test/resources/sql/misc.iq @@ -2116,6 +2116,17 @@ select !ok +# TIMESTAMPDIFF with 'flag' literal as time unit argument +SELECT TIMESTAMPDIFF(quarter, TIMESTAMP '2008-12-25', TIMESTAMP '2008-09-25'); ++--------+ +| EXPR$0 | ++--------+ +| -1 | ++--------+ +(1 row) + +! ok + # ARRAY and MULTISET select array[1,2] as a from (values (1)); +--------+
