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

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


The following commit(s) were added to refs/heads/master by this push:
     new 35c983a351 Use template file for adding table functions grammar 
(#13553)
35c983a351 is described below

commit 35c983a3510b357880cd1c4d2407d4296d054511
Author: Rohan Garg <[email protected]>
AuthorDate: Wed Dec 14 21:52:09 2022 +0530

    Use template file for adding table functions grammar (#13553)
---
 sql/edit-parser.py                     | 134 -------------
 sql/pom.xml                            |  56 +++---
 sql/src/main/codegen/config.fmpp       |   1 +
 sql/src/main/codegen/includes/from.ftl | 349 +++++++++++++++++++++++++++++++++
 4 files changed, 382 insertions(+), 158 deletions(-)

diff --git a/sql/edit-parser.py b/sql/edit-parser.py
deleted file mode 100644
index c0113b3b24..0000000000
--- a/sql/edit-parser.py
+++ /dev/null
@@ -1,134 +0,0 @@
-#! /bin/python3
-# 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.
-# ------------------------------------------------------------------------
-
-# Revise the parser to add two elements of Druid syntax to the FROM
-# clause:
-#
-# id [ (<args>) ]
-#
-# And
-#
-# TABLE(<fn>(<args>)) (<schema>)
-
-import os
-import os.path
-
-# Ensure this can run from the main pom, or within the
-# module directory.
-baseDir = ""
-if os.path.isdir("sql"):
-  baseDir = "sql/"
-source = baseDir + "target/codegen/templates/Parser.jj"
-dest = baseDir + "target/codegen/templates/DruidParser.jj"
-
-inFile = open(source)
-outFile = open(dest, "w")
-
-# Look for the rule to remove, copying lines as we go.
-while True:
-    line = inFile.readline()
-    if not line:
-        break
-    outFile.write(line)
-    if line == "SqlNode TableRef2(boolean lateral) :\n":
-        break
-
-# Find close of the rule, after the variable definitions
-while True:
-    line = inFile.readline()
-    if not line:
-        break
-    if line == "}\n":
-        break
-    outFile.write(line)
-
-outFile.write(
-'''    List<SqlNode> paramList;
-}
-''')
-
-# Find the table identifier rule
-while True:
-    line = inFile.readline()
-    if not line:
-        break
-    outFile.write(line)
-    if line == "        tableRef = CompoundIdentifier()\n":
-        break
-
-# Add the Druid parameterization
-outFile.write(
-'''        [
-            paramList = FunctionParameterList(ExprContext.ACCEPT_NONCURSOR)
-            {
-                tableRef = ParameterizeOperator.PARAM.createCall(tableRef, 
paramList);
-            }
-        ]
-''')
-
-# Skip over the unwanted EXTENDS clause
-while True:
-    line = inFile.readline()
-    if not line:
-        break
-    if line == "        over = TableOverOpt() {\n":
-        outFile.write(line)
-        break
-
-# Find the table function rule
-while True:
-    line = inFile.readline()
-    if not line:
-        break
-    outFile.write(line)
-    if line == "        tableRef = TableFunctionCall(s.pos())\n":
-        break
-
-# Find the closing paren
-while True:
-    line = inFile.readline()
-    if not line:
-        break
-    outFile.write(line)
-    if line == "        <RPAREN>\n":
-        break
-
-# Add the additional clause
-outFile.write(
-'''        [
-            [ <EXTEND> ]
-            extendList = ExtendList()
-            {
-                tableRef = ExtendOperator.EXTEND.createCall(
-                        Span.of(tableRef, extendList).pos(), tableRef, 
extendList);
-            }
-        ]
-''')
-
-# Copy everything else
-while True:
-    line = inFile.readline()
-    if not line:
-        break
-    outFile.write(line)
-
-inFile.close()
-outFile.close()
-
-# Switch the files.
-os.remove(source)
-os.rename(dest, source)
diff --git a/sql/pom.xml b/sql/pom.xml
index 960aac9f86..7c644fcdfc 100644
--- a/sql/pom.xml
+++ b/sql/pom.xml
@@ -306,30 +306,6 @@
         </executions>
       </plugin>
 
-      <!-- Edit the parser. Add an additional clause to the table function
-           rule. This produces a new parser, DruidParser.jj.
-           -->
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>exec-maven-plugin</artifactId>
-        <executions>
-          <execution>
-           <id>edit-parser</id>
-           <phase>generate-sources</phase>
-           <goals>
-             <goal>exec</goal>
-           </goals>
-           <configuration>
-             <executable>python3</executable>
-             <workingDirectory>${project.basedir}</workingDirectory>
-             <arguments>
-               <argument>edit-parser.py</argument>
-             </arguments>
-           </configuration>
-         </execution>
-        </executions>
-      </plugin>
-
       <!-- Copy the templates present in the codegen directory of druid-sql 
containing custom SQL rules to
         ${project.build.directory}/codegen -->
       <plugin>
@@ -399,6 +375,38 @@
         </executions>
       </plugin>
 
+      <!-- This plugin is used to replace the production rule for FROM clause 
in the calcite grammar.
+           It is done by a search and replace since calcite doesn't allow to 
override production rules generally
+           in its grammar (override is possible only if there's a hook for it 
in the grammar). And the FROM clause
+           doesn't contain any hook for extension. For the custom changes done 
in
+           extension, please check from.ftl file in sql module.
+      -->
+      <plugin>
+        <groupId>com.google.code.maven-replacer-plugin</groupId>
+        <artifactId>replacer</artifactId>
+        <version>1.5.3</version>
+        <executions>
+          <execution>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>replace</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          
<basedir>${project.build.directory}/generated-sources/org/apache/druid/sql/calcite/parser</basedir>
+          <includes>
+            <include>**/DruidSqlParserImpl.java</include>
+          </includes>
+          <replacements>
+            <replacement>
+              <token>fromClause = FromClause</token>
+              <value>fromClause = DruidFromClause</value>
+            </replacement>
+          </replacements>
+        </configuration>
+      </plugin>
+
       <!-- Adds the path of the generated parser to the classpath -->
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
diff --git a/sql/src/main/codegen/config.fmpp b/sql/src/main/codegen/config.fmpp
index 3a8f51189b..fce1ec2f3a 100644
--- a/sql/src/main/codegen/config.fmpp
+++ b/sql/src/main/codegen/config.fmpp
@@ -443,6 +443,7 @@ data: {
       "insert.ftl"
       "explain.ftl"
       "replace.ftl"
+      "from.ftl"
     ]
 
     includePosixOperators: false
diff --git a/sql/src/main/codegen/includes/from.ftl 
b/sql/src/main/codegen/includes/from.ftl
new file mode 100644
index 0000000000..07d3715183
--- /dev/null
+++ b/sql/src/main/codegen/includes/from.ftl
@@ -0,0 +1,349 @@
+/*
+ * 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.
+ */
+
+ // TODO jvs 15-Nov-2003:  SQL standard allows parentheses in the FROM list for
+ // building up non-linear join trees (e.g. OUTER JOIN two tables, and then 
INNER
+ // JOIN the result).  Also note that aliases on parenthesized FROM expressions
+ // "hide" all table names inside the parentheses (without aliases, they're
+ // visible).
+ //
+ // We allow CROSS JOIN to have a join condition, even though that is not valid
+ // SQL; the validator will catch it.
+
+ // DRUID NOTE : This is an implementation of the FROM clause from the 
Parser.jj file in Calcite as of version 1.21.0.
+ // This file also prefixes the required production rules with 'Druid' so that 
the whole FROM production rule can be
+ // derived from this file itself. The production clause is injected in the 
grammar using the maven replace plugin in 
+ // sql module's pom.
+
+ /**
+  * Parses the FROM clause for a SELECT.
+  *
+  * <p>FROM is mandatory in standard SQL, optional in dialects such as MySQL,
+  * PostgreSQL. The parser allows SELECT without FROM, but the validator fails
+  * if conformance is, say, STRICT_2003.
+  */
+ SqlNode DruidFromClause() :
+ {
+     SqlNode e, e2, condition;
+     SqlLiteral natural, joinType, joinConditionType;
+     SqlNodeList list;
+     SqlParserPos pos;
+ }
+ {
+     e = DruidTableRef()
+     (
+         LOOKAHEAD(2)
+         (
+             // Decide whether to read a JOIN clause or a comma, or to quit 
having
+             // seen a single entry FROM clause like 'FROM emps'. See comments
+             // elsewhere regarding <COMMA> lookahead.
+             //
+             // And LOOKAHEAD(3) is needed here rather than a LOOKAHEAD(2). 
Because currently JavaCC
+             // calculates minimum lookahead count incorrectly for choice that 
contains zero size
+             // child. For instance, with the generated code, "LOOKAHEAD(2, 
Natural(), JoinType())"
+             // returns true immediately if it sees a single "<CROSS>" token. 
Where we expect
+             // the lookahead succeeds after "<CROSS> <APPLY>".
+             //
+             // For more information about the issue, see 
https://github.com/javacc/javacc/issues/86
+             LOOKAHEAD(3)
+             natural = Natural()
+             joinType = JoinType()
+             e2 = DruidTableRef()
+             (
+                 <ON> {
+                     joinConditionType = JoinConditionType.ON.symbol(getPos());
+                 }
+                 condition = Expression(ExprContext.ACCEPT_SUB_QUERY) {
+                     e = new SqlJoin(joinType.getParserPosition(),
+                         e,
+                         natural,
+                         joinType,
+                         e2,
+                         joinConditionType,
+                         condition);
+                 }
+             |
+                 <USING> {
+                     joinConditionType = 
JoinConditionType.USING.symbol(getPos());
+                 }
+                 list = ParenthesizedSimpleIdentifierList() {
+                     e = new SqlJoin(joinType.getParserPosition(),
+                         e,
+                         natural,
+                         joinType,
+                         e2,
+                         joinConditionType,
+                         new SqlNodeList(list.getList(), 
Span.of(joinConditionType).end(this)));
+                 }
+             |
+                 {
+                     e = new SqlJoin(joinType.getParserPosition(),
+                         e,
+                         natural,
+                         joinType,
+                         e2,
+                         
JoinConditionType.NONE.symbol(joinType.getParserPosition()),
+                         null);
+                 }
+             )
+         |
+             // NOTE jvs 6-Feb-2004:  See comments at top of file for why
+             // hint is necessary here.  I had to use this special semantic
+             // lookahead form to get JavaCC to shut up, which makes
+             // me even more uneasy.
+             //LOOKAHEAD({true})
+             <COMMA> { joinType = JoinType.COMMA.symbol(getPos()); }
+             e2 = DruidTableRef() {
+                 e = new SqlJoin(joinType.getParserPosition(),
+                     e,
+                     SqlLiteral.createBoolean(false, 
joinType.getParserPosition()),
+                     joinType,
+                     e2,
+                     JoinConditionType.NONE.symbol(SqlParserPos.ZERO),
+                     null);
+             }
+         |
+             <CROSS> { joinType = JoinType.CROSS.symbol(getPos()); } <APPLY>
+             e2 = DruidTableRef2(true) {
+                 if (!this.conformance.isApplyAllowed()) {
+                     throw SqlUtil.newContextException(getPos(), 
RESOURCE.applyNotAllowed());
+                 }
+                 e = new SqlJoin(joinType.getParserPosition(),
+                     e,
+                     SqlLiteral.createBoolean(false, 
joinType.getParserPosition()),
+                     joinType,
+                     e2,
+                     JoinConditionType.NONE.symbol(SqlParserPos.ZERO),
+                     null);
+             }
+         |
+             <OUTER> { joinType = JoinType.LEFT.symbol(getPos()); } <APPLY>
+             e2 = DruidTableRef2(true) {
+                 if (!this.conformance.isApplyAllowed()) {
+                     throw SqlUtil.newContextException(getPos(), 
RESOURCE.applyNotAllowed());
+                 }
+                 e = new SqlJoin(joinType.getParserPosition(),
+                     e,
+                     SqlLiteral.createBoolean(false, 
joinType.getParserPosition()),
+                     joinType,
+                     e2,
+                     JoinConditionType.ON.symbol(SqlParserPos.ZERO),
+                     SqlLiteral.createBoolean(true, 
joinType.getParserPosition()));
+             }
+         )
+     )*
+     {
+         return e;
+     }
+ }
+
+ /**
+  * Parses a table reference in a FROM clause, not lateral unless LATERAL
+  * is explicitly specified.
+  */
+ SqlNode DruidTableRef() :
+ {
+     final SqlNode e;
+ }
+ {
+     e = DruidTableRef2(false) { return e; }
+ }
+
+ /**
+  * Parses a table reference in a FROM clause.
+  */
+ SqlNode DruidTableRef2(boolean lateral) :
+ {
+     List<SqlNode> paramList;
+     SqlNode tableRef;
+     final SqlNode over;
+     final SqlNode snapshot;
+     final SqlNode match;
+     SqlNodeList extendList = null;
+     final SqlIdentifier alias;
+     final Span s, s2;
+     SqlNodeList args;
+     SqlNode sample;
+     boolean isBernoulli;
+     SqlNumericLiteral samplePercentage;
+     boolean isRepeatable = false;
+     int repeatableSeed = 0;
+     SqlNodeList columnAliasList = null;
+     SqlUnnestOperator unnestOp = SqlStdOperatorTable.UNNEST;
+ }
+ {
+     (
+         LOOKAHEAD(2)
+         tableRef = CompoundIdentifier()
+         [
+             paramList = FunctionParameterList(ExprContext.ACCEPT_NONCURSOR)
+             {
+                 tableRef = ParameterizeOperator.PARAM.createCall(tableRef, 
paramList);
+             }
+         ]
+         over = TableOverOpt() {
+             if (over != null) {
+                 tableRef = SqlStdOperatorTable.OVER.createCall(
+                     getPos(), tableRef, over);
+             }
+         }
+         [
+             snapshot = Snapshot(tableRef) {
+                 tableRef = SqlStdOperatorTable.LATERAL.createCall(
+                     getPos(), snapshot);
+             }
+         ]
+         [
+             tableRef = MatchRecognize(tableRef)
+         ]
+     |
+         LOOKAHEAD(2)
+         [ <LATERAL> { lateral = true; } ]
+         tableRef = ParenthesizedExpression(ExprContext.ACCEPT_QUERY)
+         over = TableOverOpt()
+         {
+             if (over != null) {
+                 tableRef = SqlStdOperatorTable.OVER.createCall(
+                     getPos(), tableRef, over);
+             }
+             if (lateral) {
+                 tableRef = SqlStdOperatorTable.LATERAL.createCall(
+                     getPos(), tableRef);
+             }
+         }
+         [
+             tableRef = MatchRecognize(tableRef)
+         ]
+     |
+         <UNNEST> { s = span(); }
+         args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_SUB_QUERY)
+         [
+             <WITH> <ORDINALITY> {
+                 unnestOp = SqlStdOperatorTable.UNNEST_WITH_ORDINALITY;
+             }
+         ]
+         {
+             tableRef = unnestOp.createCall(s.end(this), args.toArray());
+         }
+     |
+         [<LATERAL> { lateral = true; } ]
+         <TABLE> { s = span(); } <LPAREN>
+         tableRef = TableFunctionCall(s.pos())
+         <RPAREN>
+         [
+             [ <EXTEND> ]
+             extendList = ExtendList()
+             {
+                 tableRef = ExtendOperator.EXTEND.createCall(
+                         Span.of(tableRef, extendList).pos(), tableRef, 
extendList);
+             }
+         ]
+         {
+             if (lateral) {
+                 tableRef = SqlStdOperatorTable.LATERAL.createCall(
+                     s.end(this), tableRef);
+             }
+         }
+     |
+         tableRef = ExtendedTableRef()
+     )
+     [
+         [ <AS> ] alias = SimpleIdentifier()
+         [ columnAliasList = ParenthesizedSimpleIdentifierList() ]
+         {
+             if (columnAliasList == null) {
+                 tableRef = SqlStdOperatorTable.AS.createCall(
+                     Span.of(tableRef).end(this), tableRef, alias);
+             } else {
+                 List<SqlNode> idList = new ArrayList<SqlNode>();
+                 idList.add(tableRef);
+                 idList.add(alias);
+                 idList.addAll(columnAliasList.getList());
+                 tableRef = SqlStdOperatorTable.AS.createCall(
+                     Span.of(tableRef).end(this), idList);
+             }
+         }
+     ]
+     [
+         <TABLESAMPLE> { s2 = span(); }
+         (
+             <SUBSTITUTE> <LPAREN> sample = StringLiteral() <RPAREN>
+             {
+                 String sampleName =
+                     SqlLiteral.unchain(sample).getValueAs(String.class);
+                 SqlSampleSpec sampleSpec = 
SqlSampleSpec.createNamed(sampleName);
+                 final SqlLiteral sampleLiteral =
+                     SqlLiteral.createSample(sampleSpec, s2.end(this));
+                 tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall(
+                     s2.add(tableRef).end(this), tableRef, sampleLiteral);
+             }
+         |
+             (
+                 <BERNOULLI>
+                 {
+                     isBernoulli = true;
+                 }
+             |
+                 <SYSTEM>
+                 {
+                     isBernoulli = false;
+                 }
+             )
+             <LPAREN> samplePercentage = UnsignedNumericLiteral() <RPAREN>
+             [
+                 <REPEATABLE> <LPAREN> repeatableSeed = IntLiteral() <RPAREN>
+                 {
+                     isRepeatable = true;
+                 }
+             ]
+             {
+                 final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100L);
+                 BigDecimal rate = samplePercentage.bigDecimalValue();
+                 if (rate.compareTo(BigDecimal.ZERO) < 0
+                     || rate.compareTo(ONE_HUNDRED) > 0)
+                 {
+                     throw SqlUtil.newContextException(getPos(), 
RESOURCE.invalidSampleSize());
+                 }
+
+                 // Treat TABLESAMPLE(0) and TABLESAMPLE(100) as no table
+                 // sampling at all.  Not strictly correct: TABLESAMPLE(0)
+                 // should produce no output, but it simplifies implementation
+                 // to know that some amount of sampling will occur.
+                 // In practice values less than ~1E-43% are treated as 0.0 and
+                 // values greater than ~99.999997% are treated as 1.0
+                 float fRate = rate.divide(ONE_HUNDRED).floatValue();
+                 if (fRate > 0.0f && fRate < 1.0f) {
+                     SqlSampleSpec tableSampleSpec =
+                     isRepeatable
+                         ? SqlSampleSpec.createTableSample(
+                             isBernoulli, fRate, repeatableSeed)
+                         : SqlSampleSpec.createTableSample(isBernoulli, fRate);
+
+                     SqlLiteral tableSampleLiteral =
+                         SqlLiteral.createSample(tableSampleSpec, 
s2.end(this));
+                     tableRef = SqlStdOperatorTable.TABLESAMPLE.createCall(
+                         s2.end(this), tableRef, tableSampleLiteral);
+                 }
+             }
+         )
+     ]
+     {
+           return tableRef;
+     }
+ }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to