You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/09/30 05:12:01 UTC

incubator-calcite git commit: [CALCITE-546] Allow table, column and field called '*'

Repository: incubator-calcite
Updated Branches:
  refs/heads/master c2950ebaf -> 1c292a9d5


[CALCITE-546] Allow table, column and field called '*'

Now a star in the SqlIdentifier's name list means a literal
star. If you want to create an identifier with a wild-card star
in it, either use the new SqlIdentifier.star method or replace
the star with an empty string.


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

Branch: refs/heads/master
Commit: 1c292a9d5225f495d319aaa581185a18ae4502aa
Parents: c2950eb
Author: Julian Hyde <jh...@apache.org>
Authored: Tue Sep 29 16:33:13 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Sep 29 20:11:23 2015 -0700

----------------------------------------------------------------------
 core/src/main/codegen/templates/Parser.jj       | 14 ++--
 .../apache/calcite/adapter/jdbc/JdbcTable.java  |  3 +-
 .../org/apache/calcite/sql/SqlIdentifier.java   | 60 ++++++++++++---
 .../apache/calcite/sql/SqlSelectOperator.java   |  5 +-
 .../calcite/sql/validate/SqlValidatorImpl.java  | 14 +++-
 .../calcite/test/SqlToRelConverterTest.java     |  4 +
 .../apache/calcite/test/SqlValidatorTest.java   | 14 +++-
 .../calcite/test/SqlToRelConverterTest.xml      | 11 +++
 core/src/test/resources/sql/misc.oq             | 77 ++++++++++++++++++++
 9 files changed, 174 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/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 cb50599..756ed6a 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -1422,13 +1422,11 @@ SqlNode SelectItem() :
 SqlNode SelectExpression() :
 {
     SqlNode e;
-    SqlParserPos pos;
 }
 {
     <STAR>
     {
-        pos = getPos();
-        return new SqlIdentifier("*", pos);
+        return SqlIdentifier.star(getPos());
     }
     |
     e = Expression(ExprContext.ACCEPT_SUBQUERY)
@@ -3520,6 +3518,7 @@ SqlIdentifier CompoundIdentifier() :
     List<String> list = new ArrayList<String>();
     List<SqlParserPos> posList = new ArrayList<SqlParserPos>();
     String p;
+    boolean star = false;
 }
 {
     p = Identifier()
@@ -3531,11 +3530,13 @@ SqlIdentifier CompoundIdentifier() :
         <DOT>
         (
             p = Identifier() {
+                star = false;
                 list.add(p);
                 posList.add(getPos());
             }
         |
             <STAR> {
+                star = true;
                 list.add("*");
                 posList.add(getPos());
             }
@@ -3543,6 +3544,9 @@ SqlIdentifier CompoundIdentifier() :
     ) *
     {
         SqlParserPos pos = SqlParserPos.sum(posList);
+        if (star) {
+            return SqlIdentifier.star(list, pos, posList);
+        }
         return new SqlIdentifier(list, null, pos, posList);
     }
 }
@@ -4140,7 +4144,7 @@ SqlNode NamedFunctionCall() :
         (
             LOOKAHEAD(2) <LPAREN> <STAR> { starPos = getPos(); } <RPAREN>
             {
-                args = startList(new SqlIdentifier("*", starPos));
+                args = startList(SqlIdentifier.star(starPos));
                 pos = pos.plus(getPos());
             }
             | LOOKAHEAD(2) <LPAREN> <RPAREN>
@@ -4394,7 +4398,7 @@ SqlNode JdbcFunctionCall() :
             LOOKAHEAD(2) <LPAREN> <STAR> {starPos = getPos();} <RPAREN>
             {
                 args = new SqlNodeList(starPos);
-                args.add(new SqlIdentifier("*", starPos));
+                args.add(SqlIdentifier.star(starPos));
             }
             | LOOKAHEAD(2) <LPAREN> <RPAREN>
             { args = new SqlNodeList(pos); }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
index f820c28..237a822 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
@@ -129,8 +129,7 @@ class JdbcTable extends AbstractQueryableTable
   SqlString generateSql() {
     final SqlNodeList selectList =
         new SqlNodeList(
-            Collections.singletonList(
-                new SqlIdentifier("*", SqlParserPos.ZERO)),
+            Collections.singletonList(SqlIdentifier.star(SqlParserPos.ZERO)),
             SqlParserPos.ZERO);
     SqlSelect node =
         new SqlSelect(SqlParserPos.ZERO, SqlNodeList.EMPTY, selectList,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java b/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
index 854b18b..9d0d633 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlIdentifier.java
@@ -24,7 +24,9 @@ import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
 import org.apache.calcite.util.Util;
 
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
 import java.util.List;
 
@@ -32,11 +34,28 @@ import java.util.List;
  * A <code>SqlIdentifier</code> is an identifier, possibly compound.
  */
 public class SqlIdentifier extends SqlNode {
+  private static final Function<String, String> STAR_TO_EMPTY =
+      new Function<String, String>() {
+        public String apply(String s) {
+          return s.equals("*") ? "" : s;
+        }
+      };
+
+  private static final Function<String, String> EMPTY_TO_STAR =
+      new Function<String, String>() {
+        public String apply(String s) {
+          return s.equals("") ? "*" : s.equals("*") ? "\"*\"" : s;
+        }
+      };
+
   //~ Instance fields --------------------------------------------------------
 
   /**
    * Array of the components of this compound identifier.
    *
+   * <p>The empty string represents the wildcard "*",
+   * to distinguish it from a real "*" (presumably specified using quotes).
+   *
    * <p>It's convenient to have this member public, and it's convenient to
    * have this member not-final, but it's a shame it's public and not-final.
    * If you assign to this member, please use
@@ -53,7 +72,7 @@ public class SqlIdentifier extends SqlNode {
   /**
    * A list of the positions of the components of compound identifiers.
    */
-  private ImmutableList<SqlParserPos> componentPositions;
+  protected ImmutableList<SqlParserPos> componentPositions;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -101,6 +120,18 @@ public class SqlIdentifier extends SqlNode {
     this(ImmutableList.of(name), null, pos, null);
   }
 
+  /** Creates an identifier that is a singleton wildcard star. */
+  public static SqlIdentifier star(SqlParserPos pos) {
+    return star(ImmutableList.of(""), pos, ImmutableList.of(pos));
+  }
+
+  /** Creates an identifier that ends in a wildcard star. */
+  public static SqlIdentifier star(List<String> names, SqlParserPos pos,
+      List<SqlParserPos> componentPositions) {
+    return new SqlIdentifier(Lists.transform(names, STAR_TO_EMPTY), null, pos,
+        componentPositions);
+  }
+
   //~ Methods ----------------------------------------------------------------
 
   public SqlKind getKind() {
@@ -112,7 +143,7 @@ public class SqlIdentifier extends SqlNode {
   }
 
   public String toString() {
-    return Util.sepList(names, ".");
+    return Util.sepList(Lists.transform(names, EMPTY_TO_STAR), ".");
   }
 
   /**
@@ -194,11 +225,18 @@ public class SqlIdentifier extends SqlNode {
   public SqlIdentifier plus(String name, SqlParserPos pos) {
     final ImmutableList<String> names =
         ImmutableList.<String>builder().addAll(this.names).add(name).build();
-    final ImmutableList.Builder<SqlParserPos> builder = ImmutableList.builder();
-    final ImmutableList<SqlParserPos> componentPositions =
-        builder.addAll(this.componentPositions).add(pos).build();
-    final SqlParserPos pos2 =
-        SqlParserPos.sum(builder.add(this.pos).build());
+    final ImmutableList<SqlParserPos> componentPositions;
+    final SqlParserPos pos2;
+    if (this.componentPositions != null) {
+      final ImmutableList.Builder<SqlParserPos> builder =
+          ImmutableList.builder();
+      componentPositions =
+          builder.addAll(this.componentPositions).add(pos).build();
+      pos2 = SqlParserPos.sum(builder.add(this.pos).build());
+    } else {
+      componentPositions = null;
+      pos2 = pos;
+    }
     return new SqlIdentifier(names, collation, pos2, componentPositions);
   }
 
@@ -216,8 +254,8 @@ public class SqlIdentifier extends SqlNode {
         writer.startList(SqlWriter.FrameTypeEnum.IDENTIFIER);
     for (String name : names) {
       writer.sep(".");
-      if (name.equals("*")) {
-        writer.print(name);
+      if (name.equals("")) {
+        writer.print("*");
       } else {
         writer.identifier(name);
       }
@@ -284,7 +322,7 @@ public class SqlIdentifier extends SqlNode {
    * Returns whether this identifier is a star, such as "*" or "foo.bar.*".
    */
   public boolean isStar() {
-    return Util.last(names).equals("*");
+    return Util.last(names).equals("");
   }
 
   /**
@@ -292,7 +330,7 @@ public class SqlIdentifier extends SqlNode {
    * "FOO.*" and "FOO.BAR" are not.
    */
   public boolean isSimple() {
-    return (names.size() == 1) && !names.get(0).equals("*");
+    return names.size() == 1 && !isStar();
   }
 
   public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java
index 858e58f..888a070 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSelectOperator.java
@@ -144,10 +144,7 @@ public class SqlSelectOperator extends SqlOperator {
     }
     SqlNode selectClause = select.selectList;
     if (selectClause == null) {
-      selectClause =
-          new SqlIdentifier(
-              "*",
-              SqlParserPos.ZERO);
+      selectClause = SqlIdentifier.star(SqlParserPos.ZERO);
     }
     final SqlWriter.Frame selectListFrame =
         writer.startList(SqlWriter.FrameTypeEnum.SELECT_LIST);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 3b3e62d..d234b6d 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -1054,7 +1054,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
         }
       }
       final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
-      selectList.add(new SqlIdentifier("*", SqlParserPos.ZERO));
+      selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
       final SqlNodeList orderList;
       if (getInnerSelect(node) != null && isAggregate(getInnerSelect(node))) {
         orderList =
@@ -1084,7 +1084,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       // (TABLE t) is equivalent to (SELECT * FROM t)
       SqlCall call = (SqlCall) node;
       final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
-      selectList.add(new SqlIdentifier("*", SqlParserPos.ZERO));
+      selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
       return new SqlSelect(SqlParserPos.ZERO, null, selectList, call.operand(0),
           null, null, null, null, null, null, null);
     }
@@ -4269,7 +4269,15 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       // Resolve rest of identifier
       for (; i < id.names.size(); i++) {
         String name = id.names.get(i);
-        final RelDataTypeField field = catalogReader.field(type, name);
+        final RelDataTypeField field;
+        if (name.equals("")) {
+          // The wildcard "*" is represented as an empty name. It never
+          // resolves to a field.
+          name = "*";
+          field = null;
+        } else {
+          field = catalogReader.field(type, name);
+        }
         if (field == null) {
           throw newValidationError(id.getComponent(i),
               RESOURCE.unknownField(name));

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index dc5b051..1494a78 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -369,6 +369,10 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
         + "group by deptno").ok();
   }
 
+  @Test public void testFakeStar() {
+    sql("SELECT * FROM (VALUES (0, 0)) AS T(A, \"*\")").ok();
+  }
+
   @Test public void testSelectDistinct() {
     sql("select distinct sal + 5 from emp").ok();
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/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 f1de2ff..a319f43 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -4550,6 +4550,16 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         "Unknown identifier 'EMPNO'");
   }
 
+  /**
+   * Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-546">[CALCITE-546]
+   * Allow table, column and field called '*'</a>.
+   */
+  @Test public void testStarIdentifier() {
+    sql("SELECT * FROM (VALUES (0, 0)) AS T(A, \"*\")")
+        .type("RecordType(INTEGER NOT NULL A, INTEGER NOT NULL *) NOT NULL");
+  }
+
   @Test public void testStarAliasFails() {
     sql("select emp.^*^ AS x from emp")
         .fails("Unknown field '\\*'");
@@ -4566,9 +4576,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
    * Parser allows "*" in FROM clause because "*" can occur in any identifier.
    * But validator must not.
    *
-   * <p>See also
-   * <a href="https://issues.apache.org/jira/browse/CALCITE-546">[CALCITE-546]
-   * Allow table, column and field called '*'</a> (not yet fixed).
+   * @see #testStarIdentifier()
    */
   @Test public void testStarInFromFails() {
     sql("select emp.empno AS x from ^sales.*^")

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 751ecfa..5912ed6 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -2520,4 +2520,15 @@ LogicalProject(EMPNO=[$0])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testFakeStar">
+        <Resource name="sql">
+            <![CDATA[SELECT * FROM (VALUES (0, 0)) AS T(A, "*")]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(A=[$0], *=[$1])
+  LogicalValues(tuples=[[{ 0, 0 }]])
+]]>
+        </Resource>
+    </TestCase>
 </Root>

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/1c292a9d/core/src/test/resources/sql/misc.oq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/misc.oq b/core/src/test/resources/sql/misc.oq
index 2b9b3c9..2704812 100644
--- a/core/src/test/resources/sql/misc.oq
+++ b/core/src/test/resources/sql/misc.oq
@@ -965,4 +965,81 @@ select * from "scott".emp where '7369' between empno and '7876';
 
 !ok
 
+# [CALCITE-546] Allow table, column and field called "*"
+# See [DRILL-3859], [DRILL-3860].
+SELECT * FROM (VALUES (0, 0)) AS T(A, "*");
++---+---+
+| A | * |
++---+---+
+| 0 | 0 |
++---+---+
+(1 row)
+
+!ok
+
+SELECT a FROM (VALUES (0, 0)) AS T(A, "*");
++---+
+| A |
++---+
+| 0 |
++---+
+(1 row)
+
+!ok
+
+SELECT b FROM (VALUES (0, 0)) AS T(A, "*");
+Column 'B' not found in any table
+!error
+
+# See [DRILL-3860].
+SELECT "a" FROM (VALUES (1, 2, 3, 4)) AS T("a", "A", ".", "*");
++---+
+| a |
++---+
+| 1 |
++---+
+(1 row)
+
+!ok
+
+SELECT "A" FROM (VALUES (1, 2, 3, 4)) AS T("a", "A", ".", "*");
++---+
+| A |
++---+
+| 2 |
++---+
+(1 row)
+
+!ok
+
+SELECT "." FROM (VALUES (1, 2, 3, 4)) AS T("a", "A", ".", "*");
++---+
+| . |
++---+
+| 3 |
++---+
+(1 row)
+
+!ok
+
+SELECT "*" FROM (VALUES (1, 2, 3, 4)) AS T("a", "A", ".", "*");
++---+
+| * |
++---+
+| 4 |
++---+
+(1 row)
+
+!ok
+
+SELECT * FROM (VALUES (1, 2, 3, 4)) AS T("a", "A", ".", "*");
++---+---+---+---+
+| a | A | . | * |
++---+---+---+---+
+| 1 | 2 | 3 | 4 |
++---+---+---+---+
+(1 row)
+
+!ok
+
 # End misc.oq