[CALCITE-2396] Allow NULL intervals in TIMESTAMPADD and DATETIME_PLUS functions (James Duong)
Fix bug where NULL can be used as an interval for these operators only for specific time units. Add a fluent API for testing validation of SQL expressions. (Julian Hyde) Close apache/calcite#2396 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/cf3eca29 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/cf3eca29 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/cf3eca29 Branch: refs/heads/master Commit: cf3eca294bedb5e268752cb54056700541177c99 Parents: 606240d Author: James Duong <[email protected]> Authored: Tue Jul 3 15:44:01 2018 -0700 Committer: Julian Hyde <[email protected]> Committed: Sun Jul 8 22:41:09 2018 -0700 ---------------------------------------------------------------------- .../sql/fun/SqlTimestampAddFunction.java | 11 +++++---- .../apache/calcite/test/SqlValidatorTest.java | 25 ++++++++++++++++++++ .../calcite/test/SqlValidatorTestCase.java | 22 ++++++++++++----- 3 files changed, 47 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/cf3eca29/core/src/main/java/org/apache/calcite/sql/fun/SqlTimestampAddFunction.java ---------------------------------------------------------------------- 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 6dc7dba..a9bf004 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 @@ -68,13 +68,13 @@ public class SqlTimestampAddFunction extends SqlFunction { public static RelDataType deduceType(RelDataTypeFactory typeFactory, TimeUnit timeUnit, RelDataType operandType1, RelDataType operandType2) { + final RelDataType type; switch (timeUnit) { case HOUR: case MINUTE: case SECOND: case MILLISECOND: case MICROSECOND: - final RelDataType type; switch (timeUnit) { case MILLISECOND: type = typeFactory.createSqlType(SqlTypeName.TIMESTAMP, @@ -87,12 +87,13 @@ public class SqlTimestampAddFunction extends SqlFunction { default: type = typeFactory.createSqlType(SqlTypeName.TIMESTAMP); } - return typeFactory.createTypeWithNullability(type, - operandType1.isNullable() - || operandType2.isNullable()); + break; default: - return operandType2; + type = operandType2; } + return typeFactory.createTypeWithNullability(type, + operandType1.isNullable() + || operandType2.isNullable()); } /** Creates a SqlTimestampAddFunction. */ http://git-wip-us.apache.org/repos/asf/calcite/blob/cf3eca29/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java index d1f0ee6..e96f1d2 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java @@ -3490,6 +3490,22 @@ public class SqlValidatorTest extends SqlValidatorTestCase { + " INTERVAL SECOND\\(1, 0\\)"); } + @Test public void testDatetimePlusNullInterval() { + expr("TIME '8:8:8' + cast(NULL AS interval hour)").columnType("TIME(0)"); + expr("TIME '8:8:8' + cast(NULL AS interval YEAR)").columnType("TIME(0)"); + expr("TIMESTAMP '1990-12-12 12:12:12' + cast(NULL AS interval hour)") + .columnType("TIMESTAMP(0)"); + expr("TIMESTAMP '1990-12-12 12:12:12' + cast(NULL AS interval YEAR)") + .columnType("TIMESTAMP(0)"); + + expr("cast(NULL AS interval hour) + TIME '8:8:8'").columnType("TIME(0)"); + expr("cast(NULL AS interval YEAR) + TIME '8:8:8'").columnType("TIME(0)"); + expr("cast(NULL AS interval hour) + TIMESTAMP '1990-12-12 12:12:12'") + .columnType("TIMESTAMP(0)"); + expr("cast(NULL AS interval YEAR) + TIMESTAMP '1990-12-12 12:12:12'") + .columnType("TIMESTAMP(0)"); + } + @Test public void testIntervalLiterals() { // First check that min, max, and defaults are what we expect // (values used in subtests depend on these being true to @@ -3682,6 +3698,15 @@ public class SqlValidatorTest extends SqlValidatorTestCase { "(?s).*Was expecting one of.*"); } + @Test public void testTimestampAddNullInterval() { + expr("timestampadd(SQL_TSI_SECOND, cast(NULL AS INTEGER)," + + " current_timestamp)") + .columnType("TIMESTAMP(0)"); + expr("timestampadd(SQL_TSI_DAY, cast(NULL AS INTEGER)," + + " current_timestamp)") + .columnType("TIMESTAMP(0)"); + } + @Test public void testNumericOperators() { // unary operator checkExpType("- cast(1 as TINYINT)", "TINYINT NOT NULL"); http://git-wip-us.apache.org/repos/asf/calcite/blob/cf3eca29/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java index 7e983dd..f09009c 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java @@ -107,7 +107,11 @@ public class SqlValidatorTestCase { } public final Sql sql(String sql) { - return new Sql(tester, sql); + return new Sql(tester, sql, true); + } + + public final Sql expr(String sql) { + return new Sql(tester, sql, false); } public final Sql winSql(String sql) { @@ -559,17 +563,23 @@ public class SqlValidatorTestCase { private final SqlTester tester; private final String sql; - Sql(SqlTester tester, String sql) { + /** Creates a Sql. + * + * @param tester Tester + * @param sql SQL query or expression + * @param query True if {@code sql} is a query, false if it is an expression + */ + Sql(SqlTester tester, String sql, boolean query) { this.tester = tester; - this.sql = sql; + this.sql = query ? sql : SqlTesterImpl.buildQuery(sql); } Sql tester(SqlTester tester) { - return new Sql(tester, sql); + return new Sql(tester, sql, true); } public Sql sql(String sql) { - return new Sql(tester, sql); + return new Sql(tester, sql, true); } Sql withExtendedCatalog() { @@ -629,7 +639,7 @@ public class SqlValidatorTestCase { * a test once at a conformance level where it fails, then run it again * at a conformance level where it succeeds. */ public Sql sansCarets() { - return new Sql(tester, sql.replace("^", "")); + return new Sql(tester, sql.replace("^", ""), true); } } }
