You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by sn...@apache.org on 2021/05/16 15:08:59 UTC

[calcite] branch master updated: [CALCITE-4603] Least restrictive for collections of collections

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

snuyanzin 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 61f8faf  [CALCITE-4603] Least restrictive for collections of collections
61f8faf is described below

commit 61f8faf590aa2b832d8e11b7fa24fd1bd0c4a7a9
Author: snuyanzin <sn...@gmail.com>
AuthorDate: Wed May 12 00:07:05 2021 +0200

    [CALCITE-4603] Least restrictive for collections of collections
    
    Close apache/calcite#2413
---
 .../calcite/rel/type/RelDataTypeFactoryImpl.java   | 49 ++++++++++++++++
 .../calcite/sql/type/SqlTypeFactoryImpl.java       | 10 +++-
 .../calcite/sql/type/SqlTypeFactoryTest.java       | 66 ++++++++++++++++++++++
 .../apache/calcite/sql/type/SqlTypeFixture.java    | 16 ++++++
 4 files changed, 140 insertions(+), 1 deletion(-)

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 41a9f68..818dd60 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
@@ -18,7 +18,10 @@ package org.apache.calcite.rel.type;
 
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.sql.SqlCollation;
+import org.apache.calcite.sql.type.ArraySqlType;
 import org.apache.calcite.sql.type.JavaToSqlTypeConversionRules;
+import org.apache.calcite.sql.type.MapSqlType;
+import org.apache.calcite.sql.type.MultisetSqlType;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeUtil;
@@ -257,6 +260,52 @@ public abstract class RelDataTypeFactoryImpl implements RelDataTypeFactory {
     return createTypeWithNullability(builder.build(), isNullable);
   }
 
+  protected @Nullable RelDataType leastRestrictiveArrayMultisetType(
+      final List<RelDataType> types, SqlTypeName sqlTypeName) {
+    assert sqlTypeName == SqlTypeName.ARRAY || sqlTypeName == SqlTypeName.MULTISET;
+    boolean isNullable = false;
+    for (RelDataType type: types) {
+      if (type.getComponentType() == null) {
+        return null;
+      }
+      isNullable |= type.isNullable();
+    }
+    final RelDataType type = leastRestrictive(
+        Util.transform(types,
+            t -> t instanceof ArraySqlType
+                ? ((ArraySqlType) t).getComponentType()
+                : ((MultisetSqlType) t).getComponentType()));
+    if (type == null) {
+      return null;
+    }
+    return sqlTypeName == SqlTypeName.ARRAY
+        ? new ArraySqlType(type, isNullable)
+        : new MultisetSqlType(type, isNullable);
+  }
+
+  protected @Nullable RelDataType leastRestrictiveMapType(
+      final List<RelDataType> types, SqlTypeName sqlTypeName) {
+    assert sqlTypeName == SqlTypeName.MAP;
+    boolean isNullable = false;
+    for (RelDataType type: types) {
+      if (!(type instanceof MapSqlType)) {
+        return null;
+      }
+      isNullable |= type.isNullable();
+    }
+    final RelDataType keyType = leastRestrictive(
+        Util.transform(types, t -> ((MapSqlType) t).getKeyType()));
+    if (keyType == null) {
+      return null;
+    }
+    final RelDataType valueType = leastRestrictive(
+        Util.transform(types, t -> ((MapSqlType) t).getValueType()));
+    if (valueType == null) {
+      return null;
+    }
+    return new MapSqlType(keyType, valueType, isNullable);
+  }
+
   // copy a non-record type, setting nullability
   private RelDataType copySimpleType(
       RelDataType type,
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java
index e20dd59..6fd58e6 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFactoryImpl.java
@@ -287,9 +287,17 @@ public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
 
       if (resultType == null) {
         resultType = type;
-        if (resultType.getSqlTypeName() == SqlTypeName.ROW) {
+        SqlTypeName sqlTypeName = resultType.getSqlTypeName();
+        if (sqlTypeName == SqlTypeName.ROW) {
           return leastRestrictiveStructuredType(types);
         }
+        if (sqlTypeName == SqlTypeName.ARRAY
+            || sqlTypeName == SqlTypeName.MULTISET) {
+          return leastRestrictiveArrayMultisetType(types, sqlTypeName);
+        }
+        if (sqlTypeName == SqlTypeName.MAP) {
+          return leastRestrictiveMapType(types, sqlTypeName);
+        }
       }
 
       RelDataTypeFamily resultFamily = resultType.getFamily();
diff --git a/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFactoryTest.java b/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFactoryTest.java
index f1c63fd..b0f9d56 100644
--- a/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFactoryTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFactoryTest.java
@@ -34,6 +34,7 @@ import java.util.Map;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.Is.is;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
@@ -83,6 +84,71 @@ class SqlTypeFactoryTest {
     assertThat(leastRestrictive.isNullable(), is(true));
   }
 
+  @Test void testLeastRestrictiveForImpossibleWithArray() {
+    SqlTypeFixture f = new SqlTypeFixture();
+    RelDataType leastRestrictive =
+        f.typeFactory.leastRestrictive(
+            Lists.newArrayList(f.arraySqlChar10, f.sqlChar));
+    assertNull(leastRestrictive);
+  }
+
+  @Test void testLeastRestrictiveForArrays() {
+    SqlTypeFixture f = new SqlTypeFixture();
+    RelDataType leastRestrictive =
+        f.typeFactory.leastRestrictive(
+            Lists.newArrayList(f.arraySqlChar10, f.arraySqlChar1));
+    assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.ARRAY));
+    assertThat(leastRestrictive.isNullable(), is(false));
+    assertThat(leastRestrictive.getComponentType().getPrecision(), is(10));
+  }
+
+  @Test void testLeastRestrictiveForMultisets() {
+    SqlTypeFixture f = new SqlTypeFixture();
+    RelDataType leastRestrictive =
+        f.typeFactory.leastRestrictive(
+            Lists.newArrayList(f.multisetSqlChar10Nullable, f.multisetSqlChar1));
+    assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.MULTISET));
+    assertThat(leastRestrictive.isNullable(), is(true));
+    assertThat(leastRestrictive.getComponentType().getPrecision(), is(10));
+  }
+
+  @Test void testLeastRestrictiveForMultisetsAndArrays() {
+    SqlTypeFixture f = new SqlTypeFixture();
+    RelDataType leastRestrictive =
+        f.typeFactory.leastRestrictive(
+            Lists.newArrayList(f.multisetSqlChar10Nullable, f.arraySqlChar1));
+    assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.MULTISET));
+    assertThat(leastRestrictive.isNullable(), is(true));
+    assertThat(leastRestrictive.getComponentType().getPrecision(), is(10));
+  }
+
+  @Test void testLeastRestrictiveForImpossibleWithMultisets() {
+    SqlTypeFixture f = new SqlTypeFixture();
+    RelDataType leastRestrictive =
+        f.typeFactory.leastRestrictive(
+            Lists.newArrayList(f.multisetSqlChar10Nullable, f.mapSqlChar1));
+    assertNull(leastRestrictive);
+  }
+
+  @Test void testLeastRestrictiveForMaps() {
+    SqlTypeFixture f = new SqlTypeFixture();
+    RelDataType leastRestrictive =
+        f.typeFactory.leastRestrictive(
+            Lists.newArrayList(f.mapSqlChar10Nullable, f.mapSqlChar1));
+    assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.MAP));
+    assertThat(leastRestrictive.isNullable(), is(true));
+    assertThat(leastRestrictive.getKeyType().getPrecision(), is(10));
+    assertThat(leastRestrictive.getValueType().getPrecision(), is(10));
+  }
+
+  @Test void testLeastRestrictiveForImpossibleWithMaps() {
+    SqlTypeFixture f = new SqlTypeFixture();
+    RelDataType leastRestrictive =
+        f.typeFactory.leastRestrictive(
+            Lists.newArrayList(f.mapSqlChar10Nullable, f.arraySqlChar1));
+    assertNull(leastRestrictive);
+  }
+
   /** Unit test for {@link SqlTypeUtil#comparePrecision(int, int)}
    * and  {@link SqlTypeUtil#maxPrecision(int, int)}. */
   @Test void testMaxPrecision() {
diff --git a/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFixture.java b/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFixture.java
index fe47de5..6827024 100644
--- a/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFixture.java
+++ b/core/src/test/java/org/apache/calcite/sql/type/SqlTypeFixture.java
@@ -78,4 +78,20 @@ class SqlTypeFixture {
       typeFactory.createMapType(sqlInt, sqlInt), false);
   final RelDataType mapOfIntNullable = typeFactory.createTypeWithNullability(
       typeFactory.createMapType(sqlInt, sqlInt), true);
+  final RelDataType sqlChar1 = typeFactory.createTypeWithNullability(
+      typeFactory.createSqlType(SqlTypeName.CHAR, 1), false);
+  final RelDataType sqlChar10 = typeFactory.createTypeWithNullability(
+      typeFactory.createSqlType(SqlTypeName.CHAR, 10), false);
+  final RelDataType arraySqlChar10 = typeFactory.createTypeWithNullability(
+      typeFactory.createArrayType(sqlChar10, -1), false);
+  final RelDataType arraySqlChar1 = typeFactory.createTypeWithNullability(
+      typeFactory.createArrayType(sqlChar1, -1), false);
+  final RelDataType multisetSqlChar10Nullable = typeFactory.createTypeWithNullability(
+      typeFactory.createMultisetType(sqlChar10, -1), true);
+  final RelDataType multisetSqlChar1 = typeFactory.createTypeWithNullability(
+      typeFactory.createMultisetType(sqlChar1, -1), false);
+  final RelDataType mapSqlChar10Nullable = typeFactory.createTypeWithNullability(
+      typeFactory.createMapType(sqlChar10, sqlChar10), true);
+  final RelDataType mapSqlChar1 = typeFactory.createTypeWithNullability(
+      typeFactory.createMapType(sqlChar1, sqlChar1), false);
 }