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

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


The following commit(s) were added to refs/heads/master by this push:
     new 37002c1  [CALCITE-3029] Java-oriented field type is wrongly forced to 
be NOT NULL after being converted to SQL-oriented
37002c1 is described below

commit 37002c1399cea1464b5d53c0fc4e5ec90f831f1b
Author: Hongze Zhang <[email protected]>
AuthorDate: Sun Apr 28 18:21:00 2019 +0800

    [CALCITE-3029] Java-oriented field type is wrongly forced to be NOT NULL 
after being converted to SQL-oriented
---
 .../apache/calcite/jdbc/JavaTypeFactoryImpl.java   | 32 +++++++++-------------
 .../apache/calcite/jdbc/JavaTypeFactoryTest.java   | 16 +++++++++++
 .../java/org/apache/calcite/tools/PlannerTest.java | 19 +++++++++++++
 3 files changed, 48 insertions(+), 19 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java 
b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
index d51de1c..40e462a 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java
@@ -37,8 +37,6 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
-import com.google.common.collect.Lists;
-
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
@@ -49,6 +47,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Implementation of {@link JavaTypeFactory}.
@@ -240,26 +239,21 @@ public class JavaTypeFactoryImpl
   /** Converts a type in Java format to a SQL-oriented type. */
   public static RelDataType toSql(final RelDataTypeFactory typeFactory,
       RelDataType type) {
-    return toSql(typeFactory, type, true);
-  }
-
-  private static RelDataType toSql(final RelDataTypeFactory typeFactory,
-      RelDataType type, boolean mustSetNullability) {
-    RelDataType sqlType = type;
     if (type instanceof RelRecordType) {
-      // We do not need to change the nullability of the nested fields,
-      // since it can be overridden by the existing implementation of 
createTypeWithNullability
-      // when we treat the nullability of the root struct type.
-      sqlType = typeFactory.createStructType(
-              Lists.transform(type.getFieldList(),
-                field -> toSql(typeFactory, field.getType(), false)),
-              type.getFieldNames());
+      return typeFactory.createTypeWithNullability(
+          typeFactory.createStructType(
+              type.getFieldList()
+                  .stream()
+                  .map(field -> toSql(typeFactory, field.getType()))
+                  .collect(Collectors.toList()),
+              type.getFieldNames()),
+          type.isNullable());
     } else if (type instanceof JavaType) {
-      sqlType = typeFactory.createSqlType(type.getSqlTypeName());
+      return typeFactory.createTypeWithNullability(
+          typeFactory.createSqlType(type.getSqlTypeName()),
+          type.isNullable());
     }
-    return mustSetNullability
-            ? typeFactory.createTypeWithNullability(sqlType, type.isNullable())
-            : sqlType;
+    return type;
   }
 
   public Type createSyntheticType(List<Type> types) {
diff --git 
a/core/src/test/java/org/apache/calcite/jdbc/JavaTypeFactoryTest.java 
b/core/src/test/java/org/apache/calcite/jdbc/JavaTypeFactoryTest.java
index cd0ba7a..0980e1d 100644
--- a/core/src/test/java/org/apache/calcite/jdbc/JavaTypeFactoryTest.java
+++ b/core/src/test/java/org/apache/calcite/jdbc/JavaTypeFactoryTest.java
@@ -18,6 +18,7 @@ package org.apache.calcite.jdbc;
 
 import org.apache.calcite.linq4j.tree.Types;
 import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.test.SqlTests;
 import org.apache.calcite.sql.type.SqlTypeName;
 
 import com.google.common.collect.ImmutableList;
@@ -73,6 +74,21 @@ public final class JavaTypeFactoryTest {
     assertRecordType(TYPE_FACTORY.getJavaClass(structWithTwoFields));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-3029";>[CALCITE-3029]
+   * Java-oriented field type is wrongly forced to be NOT NULL after being 
converted to
+   * SQL-oriented</a>. */
+  @Test public void testFieldNullabilityAfterConvertingToSqlStructType() {
+    RelDataType javaStructType = TYPE_FACTORY.createStructType(
+        ImmutableList.of(
+            TYPE_FACTORY.createJavaType(Integer.class),
+            TYPE_FACTORY.createJavaType(int.class)),
+        ImmutableList.of("a", "b"));
+    RelDataType sqlStructType = TYPE_FACTORY.toSql(javaStructType);
+    assertEquals("RecordType(INTEGER a, INTEGER NOT NULL b) NOT NULL",
+        SqlTests.getTypeString(sqlStructType));
+  }
+
   private void assertRecordType(Type actual) {
     String errorMessage =
         "Type {" + actual.getTypeName() + "} is not a subtype of 
Types.RecordType";
diff --git a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java 
b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
index 7d73397..b578e9d 100644
--- a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
+++ b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
@@ -70,6 +70,7 @@ import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParseException;
 import org.apache.calcite.sql.parser.SqlParser;
+import org.apache.calcite.sql.test.SqlTests;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlTypeName;
@@ -98,6 +99,7 @@ import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
@@ -703,6 +705,23 @@ public class PlannerTest {
             + "    EnumerableTableScan(table=[[hr, emps]])\n"));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-3029";>[CALCITE-3029]
+   * Java-oriented field type is wrongly forced to be NOT NULL after being 
converted to
+   * SQL-oriented</a>. */
+  @Test public void testInsertSourceRelTypeWithNullValues() throws Exception {
+    Planner planner = getPlanner(null, Programs.standard());
+    SqlNode parse = planner.parse(
+        "insert into \"emps\" values(1, 1, null, 1, 1)");
+    SqlNode validate = planner.validate(parse);
+    RelNode convert = planner.rel(validate).rel;
+    RelDataType insertSourceType = convert.getInput(0).getRowType();
+    String typeString = SqlTests.getTypeString(insertSourceType);
+    assertEquals("RecordType(INTEGER NOT NULL empid, INTEGER NOT NULL deptno, 
VARCHAR name, "
+            + "REAL NOT NULL salary, INTEGER commission) NOT NULL", 
typeString);
+  }
+
+
   /** Unit test that parses, validates, converts and plans. Planner is
    * provided with a list of RelTraitDefs to register. */
   @Test public void testPlanWithExplicitTraitDefs() throws Exception {

Reply via email to