This is an automated email from the ASF dual-hosted git repository.

jhyde pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git

commit f1cad57035db0f94190433aa6db2352228a641e0
Author: Julian Hyde <[email protected]>
AuthorDate: Wed Apr 24 15:54:44 2019 -0700

    [CALCITE-3022] Babel: Various SQL parsing issues
    
    * Allow 'DATE(x)' function, per PostgreSQL and Redshift
    * Allow "TIMESTAMP 'yyyy-mm-dd'" literal (missing time part), per 
PostgreSQL and Redshift
---
 babel/src/main/codegen/config.fmpp                 | 10 +++++++++
 babel/src/main/codegen/includes/parserImpls.ftl    | 20 +++++++++++++++++
 .../org/apache/calcite/test/BabelParserTest.java   | 25 +++++++++++++++++++++-
 core/src/main/codegen/config.fmpp                  |  6 ++++++
 core/src/main/codegen/templates/Parser.jj          | 14 +++---------
 .../apache/calcite/sql/parser/SqlParserUtil.java   | 15 ++++++++++---
 core/src/test/codegen/config.fmpp                  |  6 ++++++
 server/src/main/codegen/config.fmpp                |  6 ++++++
 8 files changed, 87 insertions(+), 15 deletions(-)

diff --git a/babel/src/main/codegen/config.fmpp 
b/babel/src/main/codegen/config.fmpp
index 9ff0556..d46f8ca 100644
--- a/babel/src/main/codegen/config.fmpp
+++ b/babel/src/main/codegen/config.fmpp
@@ -824,14 +824,24 @@ data: {
       ]
 
       # List of methods for parsing custom literals.
+      # Return type of method implementation should be "SqlNode".
       # Example: ParseJsonLiteral().
       literalParserMethods: [
       ]
 
       # List of methods for parsing custom data types.
+      # Return type of method implementation should be "SqlIdentifier".
+      # Example: SqlParseTimeStampZ().
       dataTypeParserMethods: [
       ]
 
+      # List of methods for parsing builtin function calls.
+      # Return type of method implementation should be "SqlNode".
+      # Example: DateFunctionCall().
+      builtinFunctionCallMethods: [
+         "DateFunctionCall()"
+      ]
+
       # List of methods for parsing extensions to "ALTER <scope>" calls.
       # Each must accept arguments "(SqlParserPos pos, String scope)".
       alterStatementParserMethods: [
diff --git a/babel/src/main/codegen/includes/parserImpls.ftl 
b/babel/src/main/codegen/includes/parserImpls.ftl
index 09b5d3d..934830a 100644
--- a/babel/src/main/codegen/includes/parserImpls.ftl
+++ b/babel/src/main/codegen/includes/parserImpls.ftl
@@ -22,4 +22,24 @@ JoinType LeftSemiJoin() :
     <LEFT> <SEMI> <JOIN> { return JoinType.LEFT_SEMI_JOIN; }
 }
 
+SqlNode DateFunctionCall() :
+{
+    final SqlFunctionCategory funcType = 
SqlFunctionCategory.USER_DEFINED_FUNCTION;
+    final SqlIdentifier qualifiedName;
+    final Span s;
+    final SqlLiteral quantifier;
+    final List<? extends SqlNode> args;
+}
+{
+    <DATE> {
+        s = span();
+        qualifiedName = new SqlIdentifier(unquotedIdentifier(), getPos());
+    }
+    args = FunctionParameterList(ExprContext.ACCEPT_SUB_QUERY) {
+        quantifier = (SqlLiteral) args.get(0);
+        args.remove(0);
+        return createCall(qualifiedName, s.end(this), funcType, quantifier, 
args);
+    }
+}
+
 // End parserImpls.ftl
diff --git a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java 
b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
index 0029f82..aceb9a2 100644
--- a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
+++ b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
@@ -155,8 +155,31 @@ public class BabelParserTest extends SqlParserTest {
         "(?s)Encountered \"when then\" at .*");
   }
 
+  /** In Redshift, DATE is a function. It requires special treatment in the
+   * parser because it is a reserved keyword.
+   * (Curiously, TIMESTAMP and TIME are not functions.) */
+  @Test public void testDateFunction() {
+    final String expected = "SELECT `DATE`(`X`)\n"
+        + "FROM `T`";
+    sql("select date(x) from t").ok(expected);
+  }
+
+  /** PostgreSQL and Redshift allow TIMESTAMP literals that contain only a
+   * date part. */
+  @Test public void testShortTimestampLiteral() {
+    sql("select timestamp '1969-07-20'")
+        .ok("SELECT TIMESTAMP '1969-07-20 00:00:00'");
+    // PostgreSQL allows the following. We should too.
+    sql("select ^timestamp '1969-07-20 1:2'^")
+        .fails("Illegal TIMESTAMP literal '1969-07-20 1:2': not in format "
+            + "'yyyy-MM-dd HH:mm:ss'"); // PostgreSQL gives 1969-07-20 01:02:00
+    sql("select ^timestamp '1969-07-20:23:'^")
+        .fails("Illegal TIMESTAMP literal '1969-07-20:23:': not in format "
+            + "'yyyy-MM-dd HH:mm:ss'"); // PostgreSQL gives 1969-07-20 23:00:00
+  }
+
   /**
-   * Babel parser's global {@code OOKAHEAD} is larger than the core
+   * Babel parser's global {@code LOOKAHEAD} is larger than the core
    * parser's. This causes different parse error message between these two
    * parsers. Here we define a looser error checker for Babel, so that we can
    * reuse failure testing codes from {@link SqlParserTest}.
diff --git a/core/src/main/codegen/config.fmpp 
b/core/src/main/codegen/config.fmpp
index 278e0d0..9687338 100644
--- a/core/src/main/codegen/config.fmpp
+++ b/core/src/main/codegen/config.fmpp
@@ -378,6 +378,12 @@ data: {
     dataTypeParserMethods: [
     ]
 
+    # List of methods for parsing builtin function calls.
+    # Return type of method implementation should be "SqlNode".
+    # Example: DateFunctionCall().
+    builtinFunctionCallMethods: [
+    ]
+
     # List of methods for parsing extensions to "ALTER <scope>" calls.
     # Each must accept arguments "(SqlParserPos pos, String scope)".
     # Example: "SqlUploadJarNode"
diff --git a/core/src/main/codegen/templates/Parser.jj 
b/core/src/main/codegen/templates/Parser.jj
index 192f65b..06cf491 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -294,16 +294,6 @@ void SqlInsertKeywords(List<SqlLiteral> keywords) :
     E()
 }
 
-SqlNode ExtendedBuiltinFunctionCall() :
-{
-}
-{
-    UnusedExtension()
-    {
-        return null;
-    }
-}
-
 /*
 * Parse Floor/Ceil function parameters
 */
@@ -4906,8 +4896,10 @@ SqlNode BuiltinFunctionCall() :
     |
         node = TimestampDiffFunctionCall() { return node; }
     |
-        node = ExtendedBuiltinFunctionCall() { return node; }
+<#list parser.builtinFunctionCallMethods as method>
+        node = ${method} { return node; }
     |
+</#list>
         node = MatchRecognizeFunctionCall() { return node; }
     |
         node = JsonExistsFunctionCall() { return node; }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java 
b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java
index 5f0f729..83c1f3a 100644
--- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java
@@ -171,9 +171,18 @@ public final class SqlParserUtil {
   public static SqlTimestampLiteral parseTimestampLiteral(String s,
       SqlParserPos pos) {
     final String dateStr = parseString(s);
-    final DateTimeUtils.PrecisionTime pt =
-        DateTimeUtils.parsePrecisionDateTimeLiteral(dateStr,
-            Format.PER_THREAD.get().timestamp, DateTimeUtils.UTC_ZONE, -1);
+    final Format format = Format.PER_THREAD.get();
+    DateTimeUtils.PrecisionTime pt = null;
+    // Allow timestamp literals with and without time fields (as does
+    // PostgreSQL); TODO: require time fields except in Babel's lenient mode
+    final DateFormat[] dateFormats = {format.timestamp, format.date};
+    for (DateFormat dateFormat : dateFormats) {
+      pt = DateTimeUtils.parsePrecisionDateTimeLiteral(dateStr,
+          dateFormat, DateTimeUtils.UTC_ZONE, -1);
+      if (pt != null) {
+        break;
+      }
+    }
     if (pt == null) {
       throw SqlUtil.newContextException(pos,
           RESOURCE.illegalLiteral("TIMESTAMP", s,
diff --git a/core/src/test/codegen/config.fmpp 
b/core/src/test/codegen/config.fmpp
index a135836..8a451e3 100644
--- a/core/src/test/codegen/config.fmpp
+++ b/core/src/test/codegen/config.fmpp
@@ -358,6 +358,12 @@ data: {
       dataTypeParserMethods: [
       ]
 
+      # List of methods for parsing builtin function calls.
+      # Return type of method implementation should be "SqlNode".
+      # Example: DateFunctionCall().
+      builtinFunctionCallMethods: [
+      ]
+
       # List of methods for parsing extensions to "ALTER <scope>" calls.
       # Each must accept arguments "(SqlParserPos pos, String scope)".
       alterStatementParserMethods: [
diff --git a/server/src/main/codegen/config.fmpp 
b/server/src/main/codegen/config.fmpp
index faa22dd..0fbb139 100644
--- a/server/src/main/codegen/config.fmpp
+++ b/server/src/main/codegen/config.fmpp
@@ -374,6 +374,12 @@ data: {
       dataTypeParserMethods: [
       ]
 
+      # List of methods for parsing builtin function calls.
+      # Return type of method implementation should be "SqlNode".
+      # Example: DateFunctionCall().
+      builtinFunctionCallMethods: [
+      ]
+
       # List of methods for parsing extensions to "ALTER <scope>" calls.
       # Each must accept arguments "(SqlParserPos pos, String scope)".
       alterStatementParserMethods: [

Reply via email to