Skip to content

Commit

Permalink
[CALCITE-4603] Least restrictive for collections of collections
Browse files Browse the repository at this point in the history
  • Loading branch information
snuyanzin committed May 16, 2021
1 parent aedb42e commit 61f8faf
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

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;
Expand Down Expand Up @@ -257,6 +260,52 @@ private RelDataType createStructType(
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,17 @@ private static void assertBasic(SqlTypeName typeName) {

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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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;

Expand Down Expand Up @@ -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() {
Expand Down
16 changes: 16 additions & 0 deletions core/src/test/java/org/apache/calcite/sql/type/SqlTypeFixture.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit 61f8faf

Please sign in to comment.