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