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

Reply via email to