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/07/13 22:44:22 UTC

[2/3] incubator-calcite git commit: [CALCITE-801] NullPointerException using USING on table alias with column aliases

[CALCITE-801] NullPointerException using USING on table alias with column aliases

Fix an issue numbering the fields in a RelCrossType


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

Branch: refs/heads/master
Commit: c57d80725766c84834a35e4f3b68feb38540b66c
Parents: 23396f0
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jul 13 12:35:46 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Jul 13 12:44:27 2015 -0700

----------------------------------------------------------------------
 .../apache/calcite/rel/type/RelCrossType.java   |  1 -
 .../rel/type/RelDataTypeFactoryImpl.java        | 48 ++++++-------
 .../calcite/sql2rel/SqlToRelConverter.java      | 65 +++++++++--------
 .../calcite/test/SqlToRelConverterTest.java     | 39 +++++++----
 .../calcite/test/SqlToRelConverterTest.xml      | 73 +++++++++++++-------
 core/src/test/resources/sql/join.oq             | 20 ++++++
 6 files changed, 148 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c57d8072/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java b/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java
index d361b7b..ba2ac85 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelCrossType.java
@@ -44,7 +44,6 @@ public class RelCrossType extends RelDataTypeImpl {
       List<RelDataTypeField> fields) {
     super(fields);
     this.types = ImmutableList.copyOf(types);
-    assert types != null;
     assert types.size() >= 1;
     for (RelDataType type : types) {
       assert !(type instanceof RelCrossType);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c57d8072/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
index e9016b8..46f8ba3 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java
@@ -126,12 +126,10 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
   public RelDataType createJoinType(RelDataType... types) {
     assert types != null;
     assert types.length >= 1;
-    final List<RelDataType> flattenedTypes =
-        getTypeArray(ImmutableList.copyOf(types));
+    final List<RelDataType> flattenedTypes = new ArrayList<>();
+    getTypeList(ImmutableList.copyOf(types), flattenedTypes);
     return canonize(
-        new RelCrossType(
-            flattenedTypes,
-            getFieldArray(flattenedTypes)));
+        new RelCrossType(flattenedTypes, getFieldList(flattenedTypes)));
   }
 
   // implement RelDataTypeFactory
@@ -359,11 +357,10 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
   }
 
   /**
-   * Returns an array of the fields in an array of types.
+   * Returns a list of the fields in a list of types.
    */
-  private static List<RelDataTypeField> getFieldArray(List<RelDataType> types) {
-    ArrayList<RelDataTypeField> fieldList =
-        new ArrayList<RelDataTypeField>();
+  private static List<RelDataTypeField> getFieldList(List<RelDataType> types) {
+    final List<RelDataTypeField> fieldList = new ArrayList<>();
     for (RelDataType type : types) {
       addFields(type, fieldList);
     }
@@ -371,20 +368,14 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
   }
 
   /**
-   * Returns an array of all atomic types in an array.
+   * Returns a list of all atomic types in a list.
    */
-  private static List<RelDataType> getTypeArray(List<RelDataType> types) {
-    List<RelDataType> flatTypes = new ArrayList<RelDataType>();
-    getTypeArray(types, flatTypes);
-    return flatTypes;
-  }
-
-  private static void getTypeArray(
-      List<RelDataType> inTypes,
+  private static void getTypeList(
+      ImmutableList<RelDataType> inTypes,
       List<RelDataType> flatTypes) {
     for (RelDataType inType : inTypes) {
       if (inType instanceof RelCrossType) {
-        getTypeArray(((RelCrossType) inType).types, flatTypes);
+        getTypeList(((RelCrossType) inType).types, flatTypes);
       } else {
         flatTypes.add(inType);
       }
@@ -392,11 +383,13 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
   }
 
   /**
-   * Adds all fields in <code>type</code> to <code>fieldList</code>.
+   * Adds all fields in <code>type</code> to <code>fieldList</code>,
+   * renumbering the fields (if necessary) to ensure that their index
+   * matches their position in the list.
    */
   private static void addFields(
       RelDataType type,
-      ArrayList<RelDataTypeField> fieldList) {
+      List<RelDataTypeField> fieldList) {
     if (type instanceof RelCrossType) {
       final RelCrossType crossType = (RelCrossType) type;
       for (RelDataType type1 : crossType.types) {
@@ -405,6 +398,10 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
     } else {
       List<RelDataTypeField> fields = type.getFieldList();
       for (RelDataTypeField field : fields) {
+        if (field.getIndex() != fieldList.size()) {
+          field = new RelDataTypeFieldImpl(field.getName(), fieldList.size(),
+              field.getType());
+        }
         fieldList.add(field);
       }
     }
@@ -415,8 +412,7 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
   }
 
   private List<RelDataTypeFieldImpl> fieldsOf(Class clazz) {
-    final List<RelDataTypeFieldImpl> list =
-        new ArrayList<RelDataTypeFieldImpl>();
+    final List<RelDataTypeFieldImpl> list = new ArrayList<>();
     for (Field field : clazz.getFields()) {
       if (Modifier.isStatic(field.getModifiers())) {
         continue;
@@ -436,8 +432,10 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
   }
 
   /**
-   * implement RelDataTypeFactory with SQL 2003 compliant behavior. Let p1, s1
-   * be the precision and scale of the first operand Let p2, s2 be the
+   * {@inheritDoc}
+   *
+   * <p>Implement RelDataTypeFactory with SQL 2003 compliant behavior. Let p1,
+   * s1 be the precision and scale of the first operand Let p2, s2 be the
    * precision and scale of the second operand Let p, s be the precision and
    * scale of the result, Then the result type is a decimal with:
    *

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c57d8072/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 5ca783f..e733d13 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -562,8 +562,8 @@ public class SqlToRelConverter {
     RelDataType validatedRowType = validator.getValidatedNodeType(query);
     validatedRowType = uniquifyFields(validatedRowType);
 
-    return RelOptUtil.equal(
-        "validated row type", validatedRowType, "converted row type", convertedRowType, false);
+    return RelOptUtil.equal("validated row type", validatedRowType,
+        "converted row type", convertedRowType, false);
   }
 
   protected RelDataType uniquifyFields(RelDataType rowType) {
@@ -1910,16 +1910,22 @@ public class SqlToRelConverter {
       RelNode rightRel = rightBlackboard.root;
       JoinRelType convertedJoinType = convertJoinType(joinType);
       RexNode conditionExp;
+      final SqlValidatorNamespace leftNamespace = validator.getNamespace(left);
+      final SqlValidatorNamespace rightNamespace = validator.getNamespace(right);
       if (isNatural) {
+        final RelDataType leftRowType = leftNamespace.getRowType();
+        final RelDataType rightRowType = rightNamespace.getRowType();
         final List<String> columnList =
-            SqlValidatorUtil.deriveNaturalJoinColumnList(
-                validator.getNamespace(left).getRowType(),
-                validator.getNamespace(right).getRowType());
-        conditionExp = convertUsing(leftRel, rightRel, columnList);
+            SqlValidatorUtil.deriveNaturalJoinColumnList(leftRowType,
+                rightRowType);
+        conditionExp = convertUsing(leftNamespace, rightNamespace,
+            columnList);
       } else {
         conditionExp =
             convertJoinCondition(
                 fromBlackboard,
+                leftNamespace,
+                rightNamespace,
                 join.getCondition(),
                 join.getConditionType(),
                 leftRel,
@@ -2253,8 +2259,9 @@ public class SqlToRelConverter {
     return Collections.emptyList();
   }
 
-  private RexNode convertJoinCondition(
-      Blackboard bb,
+  private RexNode convertJoinCondition(Blackboard bb,
+      SqlValidatorNamespace leftNamespace,
+      SqlValidatorNamespace rightNamespace,
       SqlNode condition,
       JoinConditionType conditionType,
       RelNode leftRel,
@@ -2276,7 +2283,7 @@ public class SqlToRelConverter {
         String name = id.getSimple();
         nameList.add(name);
       }
-      return convertUsing(leftRel, rightRel, nameList);
+      return convertUsing(leftNamespace, rightNamespace, nameList);
     default:
       throw Util.unexpected(conditionType);
     }
@@ -2287,37 +2294,29 @@ public class SqlToRelConverter {
    * from NATURAL JOIN. "a JOIN b USING (x, y)" becomes "a.x = b.x AND a.y =
    * b.y". Returns null if the column list is empty.
    *
-   * @param leftRel  Left input to the join
-   * @param rightRel Right input to the join
+   * @param leftNamespace Namespace of left input to join
+   * @param rightNamespace Namespace of right input to join
    * @param nameList List of column names to join on
    * @return Expression to match columns from name list, or true if name list
    * is empty
    */
-  private RexNode convertUsing(
-      RelNode leftRel,
-      RelNode rightRel,
+  private RexNode convertUsing(SqlValidatorNamespace leftNamespace,
+      SqlValidatorNamespace rightNamespace,
       List<String> nameList) {
     final List<RexNode> list = Lists.newArrayList();
     for (String name : nameList) {
-      final RelDataType leftRowType = leftRel.getRowType();
-      RelDataTypeField leftField = catalogReader.field(leftRowType, name);
-      RexNode left =
-          rexBuilder.makeInputRef(
-              leftField.getType(),
-              leftField.getIndex());
-      final RelDataType rightRowType = rightRel.getRowType();
-      RelDataTypeField rightField =
-          catalogReader.field(rightRowType, name);
-      RexNode right =
-          rexBuilder.makeInputRef(
-              rightField.getType(),
-              leftRowType.getFieldList().size() + rightField.getIndex());
-      RexNode equalsCall =
-          rexBuilder.makeCall(
-              SqlStdOperatorTable.EQUALS,
-              left,
-              right);
-      list.add(equalsCall);
+      List<RexNode> operands = new ArrayList<>();
+      int offset = 0;
+      for (SqlValidatorNamespace n : ImmutableList.of(leftNamespace,
+          rightNamespace)) {
+        final RelDataType rowType = n.getRowType();
+        final RelDataTypeField field = catalogReader.field(rowType, name);
+        operands.add(
+            rexBuilder.makeInputRef(field.getType(),
+                offset + field.getIndex()));
+        offset += rowType.getFieldList().size();
+      }
+      list.add(rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, operands));
     }
     return RexUtil.composeConjunction(rexBuilder, list, false);
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c57d8072/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 083367e..ee7f2c9 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -151,6 +151,19 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
         "${plan}");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-801">[CALCITE-801]
+   * NullPointerException using USING on table alias with column
+   * aliases</a>. */
+  @Test public void testValuesUsing() {
+    check("select d.deptno, min(e.empid) as empid\n"
+            + "from (values (100, 'Bill', 1)) as e(empid, name, deptno)\n"
+            + "join (values (1, 'LeaderShip')) as d(deptno, name)\n"
+            + "  using (deptno)\n"
+            + "group by d.deptno",
+        "${plan}");
+  }
+
   @Test public void testJoinNatural() {
     check(
         "SELECT * FROM emp NATURAL JOIN dept",
@@ -1198,7 +1211,7 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
   @Test public void testSubqueryLimitOne() {
     sql("select deptno\n"
         + "from EMP\n"
-        + "where deptno > (select deptno \n"
+        + "where deptno > (select deptno\n"
         + "from EMP order by deptno limit 1)")
         .convertsTo("${plan}");
   }
@@ -1222,10 +1235,10 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
    * Scan HAVING clause for sub-queries and IN-lists</a> relating to IN.
    */
   @Test public void testHavingAggrFunctionIn() {
-    sql("select deptno \n"
-        + "from emp \n"
-        + "group by deptno \n"
-        + "having sum(case when deptno in (1, 2) then 0 else 1 end) + \n"
+    sql("select deptno\n"
+        + "from emp\n"
+        + "group by deptno\n"
+        + "having sum(case when deptno in (1, 2) then 0 else 1 end) +\n"
         + "sum(case when deptno in (3, 4) then 0 else 1 end) > 10")
         .convertsTo("${plan}");
   }
@@ -1237,14 +1250,14 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
    * the HAVING clause.
    */
   @Test public void testHavingInSubqueryWithAggrFunction() {
-    sql("select sal \n"
-        + "from emp \n"
-        + "group by sal \n"
-        + "having sal in \n"
-            + "(select deptno \n"
-            + "from dept \n"
-            + "group by deptno \n"
-            + "having sum(deptno) > 0)")
+    sql("select sal\n"
+        + "from emp\n"
+        + "group by sal\n"
+        + "having sal in (\n"
+        + "  select deptno\n"
+        + "  from dept\n"
+        + "  group by deptno\n"
+        + "  having sum(deptno) > 0)")
         .convertsTo("${plan}");
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c57d8072/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 075ccd2..1130160 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -2438,7 +2438,7 @@ LogicalAggregate(group=[{0}], EXPR$1=[SUM($1) FILTER $2], EXPR$2=[COUNT()])
         <Resource name="sql">
             <![CDATA[select deptno
 from EMP
-where deptno > (select min(deptno) * 2 + 10 from EMP]]>
+where deptno > (select min(deptno) * 2 + 10 from EMP)]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2508,7 +2508,7 @@ LogicalProject(DEPTNO=[$7])
 from emp
 group by deptno
 having sum(case when deptno in (1, 2) then 0 else 1 end) +
-sum(case when deptno in (3, 4) then 0 else 1 end) > 10)]]>
+sum(case when deptno in (3, 4) then 0 else 1 end) > 10]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2525,11 +2525,11 @@ LogicalProject(DEPTNO=[$0])
             <![CDATA[select sal
 from emp
 group by sal
-having sal in
-(select deptno
-from dept
-group by deptno
-having sum(deptno) > 0)]]>
+having sal in (
+  select deptno
+  from dept
+  group by deptno
+  having sum(deptno) > 0)]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2633,9 +2633,10 @@ LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[INSERT], updateColu
     </TestCase>
     <TestCase name="testWindowAggWithGroupBy">
         <Resource name="sql">
-            <![CDATA[select min(deptno), rank() over (order by empno)
-            max(empno) over (partition by deptno)
-            from emp group by deptno, empno]]>
+            <![CDATA[select min(deptno), rank() over (order by empno),
+max(empno) over (partition by deptno)
+from emp group by deptno, empno
+]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2646,13 +2647,14 @@ LogicalProject(EXPR$0=[$2], EXPR$1=[RANK() OVER (ORDER BY $1 RANGE BETWEEN UNBOU
 ]]>
         </Resource>
     </TestCase>
-        <TestCase name="testWindowAggWithGroupByAndJoin">
+    <TestCase name="testWindowAggWithGroupByAndJoin">
         <Resource name="sql">
             <![CDATA[select min(d.deptno), rank() over (order by e.empno),
-            max(e.empno) over (partition by e.deptno)
-            from emp e, dept d
-            where e.deptno = d.deptno
-            group by d.deptno, e.empno, e.deptno]]>
+ max(e.empno) over (partition by e.deptno)
+from emp e, dept d
+where e.deptno = d.deptno
+group by d.deptno, e.empno, e.deptno
+]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2669,9 +2671,10 @@ LogicalProject(EXPR$0=[$3], EXPR$1=[RANK() OVER (ORDER BY $1 RANGE BETWEEN UNBOU
     <TestCase name="testWindowAggWithGroupByAndHaving">
         <Resource name="sql">
             <![CDATA[select min(deptno), rank() over (order by empno),
-            max(empno) over (partition by deptno)
-            from emp group by deptno, empno
-            having empno < 10 and min(deptno) < 20]]>
+max(empno) over (partition by deptno)
+from emp group by deptno, empno
+having empno < 10 and min(deptno) < 20
+]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2684,14 +2687,14 @@ LogicalProject(EXPR$0=[$2], EXPR$1=[RANK() OVER (ORDER BY $1 RANGE BETWEEN UNBOU
         </Resource>
     </TestCase>
     <TestCase name="testWindowAggInSubqueryJoin">
-    <Resource name="sql">
-            <![CDATA[select T.x, T.y, T.z,
-        emp.empno from (select min(deptno) as x,
-        rank() over (order by empno) as y,
-        max(empno) over (partition by deptno) as z
-        from emp group by deptno, empno) as T
-        inner join emp on T.x = emp.deptno
-        and T.y = emp.empno]]>
+        <Resource name="sql">
+            <![CDATA[select T.x, T.y, T.z, emp.empno from (select min(deptno) as x,
+   rank() over (order by empno) as y,
+   max(empno) over (partition by deptno) as z
+   from emp group by deptno, empno) as T
+ inner join emp on T.x = emp.deptno
+ and T.y = emp.empno
+]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2705,4 +2708,22 @@ LogicalProject(X=[$0], Y=[$1], Z=[$2], EMPNO=[$3])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testValuesUsing">
+        <Resource name="sql">
+            <![CDATA[select d.deptno, min(e.empid) as empid
+from (values (100, 'Bill', 1)) as e(empid, name, deptno)
+join (values (1, 'LeaderShip')) as d(deptno, name)
+  using (deptno)
+group by d.deptno]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EMPID=[MIN($1)])
+  LogicalProject(DEPTNO=[$3], EXPR$0=[$0])
+    LogicalJoin(condition=[=($2, $3)], joinType=[inner])
+      LogicalValues(tuples=[[{ 100, 'Bill', 1 }]])
+      LogicalValues(tuples=[[{ 1, 'LeaderShip' }]])
+]]>
+        </Resource>
+    </TestCase>
 </Root>

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/c57d8072/core/src/test/resources/sql/join.oq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/join.oq b/core/src/test/resources/sql/join.oq
index 97898bf..83cfb7c 100644
--- a/core/src/test/resources/sql/join.oq
+++ b/core/src/test/resources/sql/join.oq
@@ -235,4 +235,24 @@ EnumerableCalc(expr#0..3=[{inputs}], DEPTNO=[$t2], DEPTNO0=[$t0])
       EnumerableTableScan(table=[[scott, EMP]])
 !plan
 
+### [CALCITE-801] NullPointerException using USING on table alias with column aliases
+select *
+from (values (100, 'Bill', 1),
+             (200, 'Eric', 1),
+             (150, 'Sebastian', 3)) as e(empid, name, deptno)
+join (values (1, 'LeaderShip'),
+             (2, 'TestGroup'),
+             (3, 'Development')) as d(deptno, name)
+using (deptno);
++-------+-----------+--------+---------+-------------+
+| EMPID | NAME      | DEPTNO | DEPTNO0 | NAME0       |
++-------+-----------+--------+---------+-------------+
+|   100 | Bill      |      1 |       1 | LeaderShip  |
+|   150 | Sebastian |      3 |       3 | Development |
+|   200 | Eric      |      1 |       1 | LeaderShip  |
++-------+-----------+--------+---------+-------------+
+(3 rows)
+
+!ok
+
 # End join.oq