From d0bdec4f639e05498fe4ba6427fab53c826987b8 Mon Sep 17 00:00:00 2001 From: xuqianjin Date: Fri, 23 Nov 2018 22:15:20 +0800 Subject: [PATCH] [CALCITE-2699] TIMESTAMPADD function now applies to DATE and TIME as well as TIMESTAMP (xuqianjin) Close apache/calcite#936 --- .../adapter/enumerable/RexImpTable.java | 15 +++++--- .../sql/fun/SqlTimestampAddFunction.java | 12 ++++-- .../calcite/sql/test/SqlOperatorBaseTest.java | 37 +++++++++++++++++++ site/_docs/reference.md | 2 +- 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java index ada433324d5d..7776ac938b8a 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java @@ -2617,11 +2617,16 @@ public Expression implement(RexToLixTranslator translator, RexCall call, case MINUS: trop1 = Expressions.negate(trop1); } - final BuiltInMethod method = - operand0.getType().getSqlTypeName() == SqlTypeName.TIMESTAMP - ? BuiltInMethod.ADD_MONTHS - : BuiltInMethod.ADD_MONTHS_INT; - return Expressions.call(method.method, trop0, trop1); + switch (typeName) { + case TIME: + return Expressions.convert_(trop0, long.class); + default: + final BuiltInMethod method = + operand0.getType().getSqlTypeName() == SqlTypeName.TIMESTAMP + ? BuiltInMethod.ADD_MONTHS + : BuiltInMethod.ADD_MONTHS_INT; + return Expressions.call(method.method, trop0, trop1); + } case INTERVAL_DAY: case INTERVAL_DAY_HOUR: diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampAddFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampAddFunction.java index bc459d0617f4..72b00f00890b 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampAddFunction.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampAddFunction.java @@ -29,13 +29,13 @@ /** * The TIMESTAMPADD function, which adds an interval to a - * timestamp. + * datetime (TIMESTAMP, TIME or DATE). * *

The SQL syntax is * *

* TIMESTAMPADD(timestamp interval, quantity, - * timestamp) + * datetime) *
* *

The interval time unit can one of the following literals:

* - *

Returns modified timestamp. + *

Returns modified datetime. */ public class SqlTimestampAddFunction extends SqlFunction { @@ -85,7 +85,11 @@ public static RelDataType deduceType(RelDataTypeFactory typeFactory, MICROSECOND_PRECISION); break; default: - type = typeFactory.createSqlType(SqlTypeName.TIMESTAMP); + if (operandType2.getSqlTypeName() == SqlTypeName.TIME) { + type = typeFactory.createSqlType(SqlTypeName.TIME); + } else { + type = typeFactory.createSqlType(SqlTypeName.TIMESTAMP); + } } break; default: diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java index 78a2fb71374d..1a1b7501043e 100644 --- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java +++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java @@ -6957,6 +6957,43 @@ protected static Pair currentTimeString(TimeZone tz) { "2016-06-30", "DATE NOT NULL"); tester.checkScalar("timestampadd(MONTH, -1, date '2016-03-31')", "2016-02-29", "DATE NOT NULL"); + + // TIMESTAMPADD with time; returns a time value.The interval is positive. + tester.checkScalar("timestampadd(SECOND, 1, time '23:59:59')", + "00:00:00", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(MINUTE, 1, time '00:00:00')", + "00:01:00", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(MINUTE, 1, time '23:59:59')", + "00:00:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(HOUR, 1, time '23:59:59')", + "00:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(DAY, 15, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(WEEK, 3, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(MONTH, 6, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(QUARTER, 1, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(YEAR, 10, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + // TIMESTAMPADD with time; returns a time value .The interval is negative. + tester.checkScalar("timestampadd(SECOND, -1, time '00:00:00')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(MINUTE, -1, time '00:00:00')", + "23:59:00", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(HOUR, -1, time '00:00:00')", + "23:00:00", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(DAY, -1, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(WEEK, -1, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(MONTH, -1, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(QUARTER, -1, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); + tester.checkScalar("timestampadd(YEAR, -1, time '23:59:59')", + "23:59:59", "TIME(0) NOT NULL"); } @Test public void testTimestampAddFractionalSeconds() { diff --git a/site/_docs/reference.md b/site/_docs/reference.md index e43e2558fd79..ab2f4f310818 100644 --- a/site/_docs/reference.md +++ b/site/_docs/reference.md @@ -1473,7 +1473,7 @@ Not implemented: | {fn HOUR(date)} | Equivalent to `EXTRACT(HOUR FROM date)`. Returns an integer between 0 and 23. | {fn MINUTE(date)} | Equivalent to `EXTRACT(MINUTE FROM date)`. Returns an integer between 0 and 59. | {fn SECOND(date)} | Equivalent to `EXTRACT(SECOND FROM date)`. Returns an integer between 0 and 59. -| {fn TIMESTAMPADD(timeUnit, count, timestamp)} | Adds an interval of *count* *timeUnit*s to a timestamp +| {fn TIMESTAMPADD(timeUnit, count, datetime)} | Adds an interval of *count* *timeUnit*s to a datetime | {fn TIMESTAMPDIFF(timeUnit, timestamp1, timestamp2)} | Subtracts *timestamp1* from *timestamp2* and returns the result in *timeUnit*s Not implemented: