[CALCITE-1124] Add TIMESTAMPADD, TIMESTAMPDIFF functions (Arina Ielchiieva)

Close apache/calcite#204


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/0b9ea986
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/0b9ea986
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/0b9ea986

Branch: refs/heads/master
Commit: 0b9ea986b847c325da009376d3b9031c9303090e
Parents: cf5d07b
Author: Arina Ielchiieva <[email protected]>
Authored: Thu Mar 3 17:07:10 2016 +0200
Committer: Julian Hyde <[email protected]>
Committed: Mon Mar 7 21:26:48 2016 -0800

----------------------------------------------------------------------
 core/src/main/codegen/templates/Parser.jj       | 135 +++++++++++++++++++
 .../apache/calcite/sql/TimestampInterval.java   |  47 +++++++
 .../calcite/sql/fun/SqlStdOperatorTable.java    |  63 +++++++++
 .../calcite/sql/parser/SqlParserTest.java       |  41 ++++++
 .../calcite/sql/test/SqlOperatorBaseTest.java   |  15 +++
 .../apache/calcite/test/SqlValidatorTest.java   |  48 +++++++
 site/_docs/reference.md                         |  16 +++
 7 files changed, 365 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/0b9ea986/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj 
b/core/src/main/codegen/templates/Parser.jj
index ce4920b..01c35bb 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -82,6 +82,7 @@ import org.apache.calcite.sql.SqlUtil;
 import org.apache.calcite.sql.SqlWindow;
 import org.apache.calcite.sql.SqlWith;
 import org.apache.calcite.sql.SqlWithItem;
+import org.apache.calcite.sql.TimestampInterval;
 import org.apache.calcite.sql.fun.SqlCase;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.fun.SqlTrimFunction;
@@ -3438,6 +3439,64 @@ TimeUnit TimeUnit() :
     }
 }
 
+SqlLiteral TimestampInterval() :
+{
+SqlLiteral.SqlSymbol symbol = null;
+}
+{
+    (
+        ( <FRAC_SECOND> | <MICROSECOND> | <SQL_TSI_FRAC_SECOND> | 
<SQL_TSI_MICROSECOND> )
+            {
+                symbol = TimestampInterval.MICROSECOND;
+            }
+        |
+        ( <SECOND> | <SQL_TSI_SECOND> )
+            {
+                symbol = TimestampInterval.SECOND;
+            }
+        |
+        ( <MINUTE> | <SQL_TSI_MINUTE> )
+            {
+                symbol = TimestampInterval.MINUTE;
+            }
+        |
+        ( <HOUR> | <SQL_TSI_HOUR> )
+            {
+                symbol = TimestampInterval.HOUR;
+            }
+        |
+        ( <DAY> | <SQL_TSI_DAY> )
+            {
+                symbol = TimestampInterval.DAY;
+            }
+        |
+         (<WEEK> | <SQL_TSI_WEEK> )
+            {
+                symbol = TimestampInterval.WEEK;
+            }
+        |
+        ( <MONTH> | <SQL_TSI_MONTH> )
+            {
+                symbol = TimestampInterval.MONTH;
+            }
+        |
+        ( <QUARTER> | <SQL_TSI_QUARTER> )
+            {
+                symbol = TimestampInterval.QUARTER;
+            }
+        |
+        ( <YEAR> | <SQL_TSI_YEAR> )
+            {
+                symbol = TimestampInterval.YEAR;
+            }
+    )
+    {
+        return SqlLiteral.createSymbol(symbol, getPos());
+    }
+}
+
+
+
 /**
  * Parses a dynamic parameter marker.
  */
@@ -4143,6 +4202,50 @@ SqlNode BuiltinFunctionCall() :
         }
     )
     |
+    (
+        <TIMESTAMPADD>
+            {
+                pos = getPos();
+                SqlLiteral interval;
+            }
+            <LPAREN>
+                interval = TimestampInterval()
+                { args = startList(interval); }
+             <COMMA>
+                 e = Expression(ExprContext.ACCEPT_SUBQUERY)
+                 { args.add(e); }
+             <COMMA>
+                 e = Expression(ExprContext.ACCEPT_SUBQUERY)
+                 { args.add(e); }
+             <RPAREN>
+           {
+              return SqlStdOperatorTable.TIMESTAMP_ADD.createCall(
+                 pos, SqlParserUtil.toNodeArray(args));
+           }
+    )
+    |
+    (
+        <TIMESTAMPDIFF>
+            {
+                pos = getPos();
+                SqlLiteral interval;
+            }
+            <LPAREN>
+                interval = TimestampInterval()
+                { args = startList(interval); }
+             <COMMA>
+                 e = Expression(ExprContext.ACCEPT_SUBQUERY)
+                 { args.add(e); }
+             <COMMA>
+                 e = Expression(ExprContext.ACCEPT_SUBQUERY)
+                 { args.add(e); }
+             <RPAREN>
+           {
+              return SqlStdOperatorTable.TIMESTAMP_DIFF.createCall(
+                 pos, SqlParserUtil.toNodeArray(args));
+           }
+    )
+    |
     {
         SqlNode node;
     }
@@ -4810,6 +4913,7 @@ SqlPostfixOperator PostfixRowOperator() :
     | < FOREIGN: "FOREIGN" >
     | < FORTRAN: "FORTRAN" >
     | < FOUND: "FOUND" >
+    | < FRAC_SECOND: "FRAC_SECOND" >
     | < FREE: "FREE" >
     | < FROM: "FROM" >
     | < FULL: "FULL" >
@@ -4892,6 +4996,7 @@ SqlPostfixOperator PostfixRowOperator() :
     | < MESSAGE_OCTET_LENGTH: "MESSAGE_OCTET_LENGTH" >
     | < MESSAGE_TEXT: "MESSAGE_TEXT" >
     | < METHOD: "METHOD" >
+    | < MICROSECOND: "MICROSECOND" >
     | < MIN: "MIN" >
     | < MINUTE: "MINUTE" >
     | < MINVALUE: "MINVALUE" >
@@ -4975,6 +5080,7 @@ SqlPostfixOperator PostfixRowOperator() :
     | < PRIVILEGES: "PRIVILEGES" >
     | < PROCEDURE: "PROCEDURE" >
     | < PUBLIC: "PUBLIC" >
+    | < QUARTER: "QUARTER" >
     | < RANGE: "RANGE" >
     | < RANK: "RANK" >
     | < READ: "READ" >
@@ -5057,6 +5163,16 @@ SqlPostfixOperator PostfixRowOperator() :
     | < SQLEXCEPTION: "SQLEXCEPTION" >
     | < SQLSTATE: "SQLSTATE" >
     | < SQLWARNING: "SQLWARNING" >
+    | < SQL_TSI_DAY: "SQL_TSI_DAY" >
+    | < SQL_TSI_FRAC_SECOND: "SQL_TSI_FRAC_SECOND" >
+    | < SQL_TSI_HOUR: "SQL_TSI_HOUR" >
+    | < SQL_TSI_MICROSECOND: "SQL_TSI_MICROSECOND" >
+    | < SQL_TSI_MINUTE: "SQL_TSI_MINUTE" >
+    | < SQL_TSI_MONTH: "SQL_TSI_MONTH" >
+    | < SQL_TSI_QUARTER: "SQL_TSI_QUARTER" >
+    | < SQL_TSI_SECOND: "SQL_TSI_SECOND" >
+    | < SQL_TSI_WEEK: "SQL_TSI_WEEK" >
+    | < SQL_TSI_YEAR: "SQL_TSI_YEAR" >
     | < SQRT: "SQRT" >
     | < START: "START" >
     | < STATE: "STATE" >
@@ -5083,6 +5199,8 @@ SqlPostfixOperator PostfixRowOperator() :
     | < TIES: "TIES" >
     | < TIME: "TIME" >
     | < TIMESTAMP: "TIMESTAMP" >
+    | < TIMESTAMPADD: "TIMESTAMPADD" >
+    | < TIMESTAMPDIFF: "TIMESTAMPDIFF" >
     | < TIMEZONE_HOUR: "TIMEZONE_HOUR" >
     | < TIMEZONE_MINUTE: "TIMEZONE_MINUTE" >
     | < TINYINT: "TINYINT" >
@@ -5133,6 +5251,7 @@ SqlPostfixOperator PostfixRowOperator() :
     | < VARYING: "VARYING" >
     | < VERSION: "VERSION" >
     | < VIEW: "VIEW" >
+    | < WEEK: "WEEK" >
     | < WHEN: "WHEN" >
     | < WHENEVER: "WHENEVER" >
     | < WHERE: "WHERE" >
@@ -5254,6 +5373,7 @@ String CommonNonReservedKeyWord() :
         | <FOLLOWING>
         | <FORTRAN>
         | <FOUND>
+        | <FRAC_SECOND>
         | <G>
         | <GENERAL>
         | <GENERATED>
@@ -5286,6 +5406,7 @@ String CommonNonReservedKeyWord() :
         | <MAP>
         | <MATCHED>
         | <MAXVALUE>
+        | <MICROSECOND>
         | <MESSAGE_LENGTH>
         | <MESSAGE_OCTET_LENGTH>
         | <MESSAGE_TEXT>
@@ -5327,6 +5448,7 @@ String CommonNonReservedKeyWord() :
         | <PRIOR>
         | <PRIVILEGES>
         | <PUBLIC>
+        | <QUARTER>
         | <READ>
         | <RELATIVE>
         | <REPEATABLE>
@@ -5362,6 +5484,16 @@ String CommonNonReservedKeyWord() :
         | <SOURCE>
         | <SPACE>
         | <SPECIFIC_NAME>
+        | <SQL_TSI_DAY>
+        | <SQL_TSI_FRAC_SECOND>
+        | <SQL_TSI_HOUR>
+        | <SQL_TSI_MICROSECOND>
+        | <SQL_TSI_MINUTE>
+        | <SQL_TSI_MONTH>
+        | <SQL_TSI_QUARTER>
+        | <SQL_TSI_SECOND>
+        | <SQL_TSI_WEEK>
+        | <SQL_TSI_YEAR>
         | <STATE>
         | <STATEMENT>
         | <STRUCTURE>
@@ -5371,6 +5503,8 @@ String CommonNonReservedKeyWord() :
         | <TABLE_NAME>
         | <TEMPORARY>
         | <TIES>
+        | <TIMESTAMPADD>
+        | <TIMESTAMPDIFF>
         | <TOP_LEVEL_COUNT>
         | <TRANSACTION>
         | <TRANSACTIONS_ACTIVE>
@@ -5393,6 +5527,7 @@ String CommonNonReservedKeyWord() :
         | <USER_DEFINED_TYPE_SCHEMA>
         | <VERSION>
         | <VIEW>
+        | <WEEK>
         | <WRAPPER>
         | <WORK>
         | <WRITE>

http://git-wip-us.apache.org/repos/asf/calcite/blob/0b9ea986/core/src/main/java/org/apache/calcite/sql/TimestampInterval.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/TimestampInterval.java 
b/core/src/main/java/org/apache/calcite/sql/TimestampInterval.java
new file mode 100644
index 0000000..d586d4a
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/TimestampInterval.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql;
+
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+/**
+ * Enumerates the timestamp intervals.
+ */
+public enum TimestampInterval implements SqlLiteral.SqlSymbol  {
+
+  MICROSECOND,
+  SECOND,
+  MINUTE,
+  HOUR,
+  DAY,
+  WEEK,
+  MONTH,
+  QUARTER,
+  YEAR;
+
+  /**
+   * Creates a parse-tree node representing an occurrence of this
+   * condition type keyword at a particular position in the parsed
+   * text.
+   */
+  public SqlLiteral symbol(SqlParserPos pos) {
+    return SqlLiteral.createSymbol(this, pos);
+  }
+
+}
+
+// End TimestampInterval.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/0b9ea986/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index ece34a5..98d2e85 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -46,6 +46,7 @@ import org.apache.calcite.sql.type.InferTypes;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlOperandCountRanges;
+import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable;
 import org.apache.calcite.sql.validate.SqlModality;
@@ -1315,6 +1316,68 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
       new SqlCurrentDateFunction();
 
   /**
+   * <p>Timestamp modifying or calculating difference functions.
+   * As first parameter take timestamp interval.
+   * </p>
+   *
+   * <p>Interval can one of the following literals:<br>
+   * <i>FRAC_SECOND, MICROSECOND, SQL_TSI_FRAC_SECOND, 
SQL_TSI_MICROSECOND</i><br>
+   * <i>SECOND, SQL_TSI_SECOND</i><br>
+   * <i>MINUTE, SQL_TSI_MINUTE</i><br>
+   * <i>HOUR, SQL_TSI_HOUR</i><br>
+   * <i>DAY, SQL_TSI_DAY</i><br>
+   * <i>WEEK, SQL_TSI_WEEK</i><br>
+   * <i>MONTH, SQL_TSI_MONTH</i><br>
+   * <i>QUARTER, SQL_TSI_QUARTER</i><br>
+   * <i>YEAR, SQL_TSI_YEAR</i><br>
+   * </p>
+   */
+
+  /**
+   * <p>The SQL <code>TIMESTAMP_ADD</code> function.
+   * Adds interval to timestamp.</p>
+   *
+   * <p>The SQL syntax is
+   *
+   * <blockquote>
+   * <code>TIMESTAMP_ADD(<i>timestamp 
interval</i>,<i>quantity</i>,<i>timestamp</i>)</code>
+   * </blockquote><br>
+   *
+   * Returns modified timestamp.</p>
+   */
+  public static final SqlFunction TIMESTAMP_ADD =
+      new SqlFunction(
+          "TIMESTAMPADD",
+          SqlKind.OTHER_FUNCTION,
+          ReturnTypes.ARG2,
+          null,
+          OperandTypes.family(
+              SqlTypeFamily.ANY, SqlTypeFamily.INTEGER, 
SqlTypeFamily.DATETIME),
+          SqlFunctionCategory.TIMEDATE);
+
+  /**
+   * <p>The SQL <code>TIMESTAMP_DIFF</code> function.
+   * Calculates difference between two timestamps.</p>
+   *
+   * <p>The SQL syntax is
+   *
+   * <blockquote>
+   * <code>TIMESTAMP_DIFF(<i>timestamp 
interval</i>,<i>timestamp</i>,<i>timestamp</i>)</code>
+   * </blockquote><br>
+   *
+   * Returns difference between two timestamps in indicated timestamp 
interval.</p>
+   */
+  public static final SqlFunction TIMESTAMP_DIFF = new SqlFunction(
+      "TIMESTAMPDIFF",
+      SqlKind.OTHER_FUNCTION,
+      ReturnTypes.INTEGER_NULLABLE,
+      null,
+      OperandTypes.family(
+          SqlTypeFamily.ANY, SqlTypeFamily.DATETIME, SqlTypeFamily.DATETIME),
+      SqlFunctionCategory.TIMEDATE);
+
+
+  /**
    * Use of the <code>IN_FENNEL</code> operator forces the argument to be
    * evaluated in Fennel. Otherwise acts as identity function.
    */

http://git-wip-us.apache.org/repos/asf/calcite/blob/0b9ea986/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java 
b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index 31dbd58..858e8a0 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -29,6 +29,9 @@ import org.apache.calcite.util.ConversionUtil;
 import org.apache.calcite.util.TestUtil;
 import org.apache.calcite.util.Util;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -39,7 +42,10 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertEquals;
@@ -5525,6 +5531,41 @@ public class SqlParserTest {
         "CAST(INTERVAL '3-2' YEAR TO MONTH AS CHAR(5))");
   }
 
+  @Test public void testTimestampAddAndDiff() {
+    Map<String, List<String>> tsi = ImmutableMap.<String, 
List<String>>builder()
+        .put("MICROSECOND",
+            Arrays.asList("FRAC_SECOND", "MICROSECOND",
+                "SQL_TSI_FRAC_SECOND", "SQL_TSI_MICROSECOND"))
+        .put("SECOND", Arrays.asList("SECOND", "SQL_TSI_SECOND"))
+        .put("MINUTE", Arrays.asList("MINUTE", "SQL_TSI_MINUTE"))
+        .put("HOUR", Arrays.asList("HOUR", "SQL_TSI_HOUR"))
+        .put("DAY", Arrays.asList("DAY", "SQL_TSI_DAY"))
+        .put("WEEK", Arrays.asList("WEEK", "SQL_TSI_WEEK"))
+        .put("MONTH", Arrays.asList("MONTH", "SQL_TSI_MONTH"))
+        .put("QUARTER", Arrays.asList("QUARTER", "SQL_TSI_QUARTER"))
+        .put("YEAR", Arrays.asList("YEAR", "SQL_TSI_YEAR"))
+        .build();
+
+    List<String> functions = ImmutableList.<String>builder()
+        .add("timestampadd(%1$s, 12, %2$scurrent_timestamp%2$s)")
+        .add("timestampdiff(%1$s, %2$scurrent_timestamp%2$s, 
%2$scurrent_timestamp%2$s)")
+        .build();
+
+    for (Map.Entry<String, List<String>> intervalGroup : tsi.entrySet()) {
+      for (String function : functions) {
+        for (String interval : intervalGroup.getValue()) {
+          checkExp(String.format(function, interval, ""),
+              String.format(function, intervalGroup.getKey(), 
"`").toUpperCase());
+        }
+      }
+    }
+
+    checkExpFails("timestampadd(^incorrect^, 1, current_timestamp)",
+        "(?s).*Was expecting one of.*");
+    checkExpFails("timestampdiff(^incorrect^, current_timestamp, 
current_timestamp)",
+        "(?s).*Was expecting one of.*");
+  }
+
   @Test public void testUnnest() {
     check(
         "select*from unnest(x)",

http://git-wip-us.apache.org/repos/asf/calcite/blob/0b9ea986/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
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 81bfeed..48311dd 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
@@ -4599,6 +4599,21 @@ public abstract class SqlOperatorBaseTest {
         "floor(cast(null as interval year))");
   }
 
+  @Test public void testTimestampAddAdnDiff() {
+    if (!enable) {
+      return;
+    }
+    tester.checkScalar(
+        "timestampadd(MINUTE, 2, timestamp '2016-02-24 12:42:25')",
+        "2016-02-24 12:42:27",
+        "TIMESTAMP(0) NOT NULL");
+    tester.checkScalar(
+        "timestampdiff(YEAR, "
+            + "timestamp '2014-02-24 12:42:25', "
+            + "timestamp '2016-02-24 12:42:25')",
+        "2", "INTEGER NOT NULL");
+  }
+
   @Test public void testDenseRankFunc() {
     tester.setFor(
         SqlStdOperatorTable.DENSE_RANK, VM_FENNEL, VM_JAVA);

http://git-wip-us.apache.org/repos/asf/calcite/blob/0b9ea986/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 b3a8699..c8ba8e9 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -3589,6 +3589,54 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         "(?s).*Cannot apply '/' to arguments of type '<DECIMAL.4, 3.> / 
<INTERVAL DAY TO SECOND>'.*");
   }
 
+  @Test public void testTimestampAddAndDiff() {
+    List<String> tsi = ImmutableList.<String>builder()
+        .add("FRAC_SECOND")
+        .add("MICROSECOND")
+        .add("MINUTE")
+        .add("HOUR")
+        .add("DAY")
+        .add("WEEK")
+        .add("MONTH")
+        .add("QUARTER")
+        .add("YEAR")
+        .add("SQL_TSI_FRAC_SECOND")
+        .add("SQL_TSI_MICROSECOND")
+        .add("SQL_TSI_MINUTE")
+        .add("SQL_TSI_HOUR")
+        .add("SQL_TSI_DAY")
+        .add("SQL_TSI_WEEK")
+        .add("SQL_TSI_MONTH")
+        .add("SQL_TSI_QUARTER")
+        .add("SQL_TSI_YEAR")
+        .build();
+
+    List<String> functions = ImmutableList.<String>builder()
+        .add("timestampadd(%s, 12, current_timestamp)")
+        .add("timestampdiff(%s, current_timestamp, current_timestamp)")
+        .build();
+
+    for (String interval : tsi) {
+      for (String function : functions) {
+        checkExp(String.format(function, interval));
+      }
+    }
+
+    checkExpType(
+        "timestampadd(SQL_TSI_WEEK, 2, current_timestamp)", "TIMESTAMP(0) NOT 
NULL");
+    checkExpType(
+        "timestampadd(SQL_TSI_WEEK, 2, cast(null as timestamp))", 
"TIMESTAMP(0)");
+    checkExpType(
+        "timestampdiff(SQL_TSI_WEEK, current_timestamp, current_timestamp)", 
"INTEGER NOT NULL");
+    checkExpType(
+        "timestampdiff(SQL_TSI_WEEK, cast(null as timestamp), 
current_timestamp)", "INTEGER");
+
+    checkWholeExpFails("timestampadd(incorrect, 1, current_timestamp)",
+        "(?s).*Was expecting one of.*");
+    checkWholeExpFails("timestampdiff(incorrect, current_timestamp, 
current_timestamp)",
+        "(?s).*Was expecting one of.*");
+  }
+
   @Test public void testNumericOperators() {
     // unary operator
     checkExpType("- cast(1 as TINYINT)", "TINYINT NOT NULL");

http://git-wip-us.apache.org/repos/asf/calcite/blob/0b9ea986/site/_docs/reference.md
----------------------------------------------------------------------
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 7bc9bc3..8785bd5 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -379,6 +379,7 @@ FOLLOWING,
 **FOREIGN**,
 FORTRAN,
 FOUND,
+FRAC_SECOND,
 **FREE**,
 **FROM**,
 **FULL**,
@@ -461,6 +462,7 @@ MESSAGE_LENGTH,
 MESSAGE_OCTET_LENGTH,
 MESSAGE_TEXT,
 **METHOD**,
+MICROSECOND,
 **MIN**,
 **MINUTE**,
 MINVALUE,
@@ -544,6 +546,7 @@ PRIOR,
 PRIVILEGES,
 **PROCEDURE**,
 PUBLIC,
+QUARTER,
 **RANGE**,
 **RANK**,
 READ,
@@ -626,6 +629,16 @@ SPECIFIC_NAME,
 **SQLEXCEPTION**,
 **SQLSTATE**,
 **SQLWARNING**,
+SQL_TSI_DAY,
+SQL_TSI_FRAC_SECOND,
+SQL_TSI_HOUR,
+SQL_TSI_MICROSECOND,
+SQL_TSI_MINUTE,
+SQL_TSI_MONTH,
+SQL_TSI_QUARTER,
+SQL_TSI_SECOND,
+SQL_TSI_WEEK,
+SQL_TSI_YEAR,
 **SQRT**,
 **START**,
 STATE,
@@ -652,6 +665,8 @@ TEMPORARY,
 TIES,
 **TIME**,
 **TIMESTAMP**,
+TIMESTAMPADD,
+TIMESTAMPDIFF,
 **TIMEZONE_HOUR**,
 **TIMEZONE_MINUTE**,
 **TINYINT**,
@@ -702,6 +717,7 @@ USER_DEFINED_TYPE_SCHEMA,
 **VAR_SAMP**,
 VERSION,
 VIEW,
+WEEK,
 **WHEN**,
 **WHENEVER**,
 **WHERE**,

Reply via email to