blob: a85fff1bf064f01e90c7c5e81c1df6971c8f2f2c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.sql.type;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
import org.apache.calcite.rel.type.RelRecordType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasToString;
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;
/**
* Test for {@link SqlTypeFactoryImpl}.
*/
class SqlTypeFactoryTest {
@Test void testLeastRestrictiveWithAny() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(Lists.newArrayList(f.sqlBigInt, f.sqlAny));
assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.ANY));
}
@Test void testLeastRestrictiveWithNumbers() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(Lists.newArrayList(f.sqlBigInt, f.sqlInt));
assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.BIGINT));
}
@Test void testLeastRestrictiveWithNullability() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(Lists.newArrayList(f.sqlVarcharNullable, f.sqlAny));
assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.ANY));
assertThat(leastRestrictive.isNullable(), is(true));
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-2994">[CALCITE-2994]
* Least restrictive type among structs does not consider nullability</a>. */
@Test void testLeastRestrictiveWithNullableStruct() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(ImmutableList.of(f.structOfIntNullable, f.structOfInt));
assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.ROW));
assertThat(leastRestrictive.isNullable(), is(true));
}
@Test void testLeastRestrictiveWithNull() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(Lists.newArrayList(f.sqlNull, f.sqlNull));
assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.NULL));
assertThat(leastRestrictive.isNullable(), is(true));
}
@Test void testLeastRestrictiveStructWithNull() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(Lists.newArrayList(f.sqlNull, f.structOfInt));
assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.ROW));
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 testLeastRestrictiveForTimestamps() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(
Lists.newArrayList(f.sqlTimestampPrec0, f.sqlTimestampPrec3));
assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.TIMESTAMP));
assertThat(leastRestrictive.isNullable(), is(false));
assertThat(leastRestrictive.getPrecision(), is(3));
}
@Test void testLeastRestrictiveForTimestamps2() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(
Lists.newArrayList(f.sqlTimestampPrec3, f.sqlTimestampPrec0));
assertThat(leastRestrictive.getSqlTypeName(), is(SqlTypeName.TIMESTAMP));
assertThat(leastRestrictive.isNullable(), is(false));
assertThat(leastRestrictive.getPrecision(), is(3));
}
@Test void testLeastRestrictiveForTimestampAndDate() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
f.typeFactory.leastRestrictive(
Lists.newArrayList(f.sqlTimestampPrec3, f.sqlDate));
assertNull(leastRestrictive);
}
@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() {
final int un = RelDataType.PRECISION_NOT_SPECIFIED;
checkPrecision(1, 1, 1, 0);
checkPrecision(2, 1, 2, 1);
checkPrecision(2, 100, 100, -1);
checkPrecision(2, un, un, -1);
checkPrecision(un, 2, un, 1);
checkPrecision(un, un, un, 0);
}
/** Unit test for {@link ArraySqlType#getPrecedenceList()}. */
@Test void testArrayPrecedenceList() {
SqlTypeFixture f = new SqlTypeFixture();
assertThat(checkPrecendenceList(f.arrayBigInt, f.arrayBigInt, f.arrayFloat),
is(3));
assertThat(
checkPrecendenceList(f.arrayOfArrayBigInt, f.arrayOfArrayBigInt,
f.arrayOfArrayFloat), is(3));
assertThat(checkPrecendenceList(f.sqlBigInt, f.sqlBigInt, f.sqlFloat),
is(3));
assertThat(
checkPrecendenceList(f.multisetBigInt, f.multisetBigInt,
f.multisetFloat), is(3));
assertThat(
checkPrecendenceList(f.arrayBigInt, f.arrayBigInt,
f.arrayBigIntNullable), is(0));
try {
int i = checkPrecendenceList(f.arrayBigInt, f.sqlBigInt, f.sqlInt);
fail("Expected assert, got " + i);
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), is("must contain type: BIGINT"));
}
}
private int checkPrecendenceList(RelDataType t, RelDataType type1, RelDataType type2) {
return t.getPrecedenceList().compareTypePrecedence(type1, type2);
}
private void checkPrecision(int p0, int p1, int expectedMax,
int expectedComparison) {
assertThat(SqlTypeUtil.maxPrecision(p0, p1), is(expectedMax));
assertThat(SqlTypeUtil.maxPrecision(p1, p0), is(expectedMax));
assertThat(SqlTypeUtil.maxPrecision(p0, p0), is(p0));
assertThat(SqlTypeUtil.maxPrecision(p1, p1), is(p1));
assertThat(SqlTypeUtil.comparePrecision(p0, p1), is(expectedComparison));
assertThat(SqlTypeUtil.comparePrecision(p0, p0), is(0));
assertThat(SqlTypeUtil.comparePrecision(p1, p1), is(0));
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-2464">[CALCITE-2464]
* Allow to set nullability for columns of structured types</a>. */
@Test void createStructTypeWithNullability() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataTypeFactory typeFactory = f.typeFactory;
List<RelDataTypeField> fields = new ArrayList<>();
RelDataTypeField field0 =
new RelDataTypeFieldImpl("i", 0,
typeFactory.createSqlType(SqlTypeName.INTEGER));
RelDataTypeField field1 =
new RelDataTypeFieldImpl("s", 1,
typeFactory.createSqlType(SqlTypeName.VARCHAR));
fields.add(field0);
fields.add(field1);
final RelDataType recordType = new RelRecordType(fields); // nullable false by default
final RelDataType copyRecordType =
typeFactory.createTypeWithNullability(recordType, true);
assertFalse(recordType.isNullable());
assertTrue(copyRecordType.isNullable());
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-3429">[CALCITE-3429]
* AssertionError thrown for user-defined table function with map argument</a>. */
@Test void testCreateTypeWithJavaMapType() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType relDataType = f.typeFactory.createJavaType(Map.class);
assertThat(relDataType.getSqlTypeName(), is(SqlTypeName.MAP));
assertThat(relDataType.getKeyType().getSqlTypeName(), is(SqlTypeName.ANY));
try {
f.typeFactory.createSqlType(SqlTypeName.MAP);
fail();
} catch (AssertionError e) {
assertThat(e.getMessage(), is("use createMapType() instead"));
}
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-3924">[CALCITE-3924]
* Fix flakey test to handle TIMESTAMP and TIMESTAMP(0) correctly</a>. */
@Test void testCreateSqlTypeWithPrecision() {
SqlTypeFixture f = new SqlTypeFixture();
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIME);
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIMESTAMP);
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIME_TZ);
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
checkCreateSqlTypeWithPrecision(f.typeFactory, SqlTypeName.TIMESTAMP_TZ);
}
private void checkCreateSqlTypeWithPrecision(
RelDataTypeFactory typeFactory, SqlTypeName sqlTypeName) {
RelDataType ts = typeFactory.createSqlType(sqlTypeName);
RelDataType tsWithoutPrecision = typeFactory.createSqlType(sqlTypeName, -1);
RelDataType tsWithPrecision0 = typeFactory.createSqlType(sqlTypeName, 0);
RelDataType tsWithPrecision1 = typeFactory.createSqlType(sqlTypeName, 1);
RelDataType tsWithPrecision2 = typeFactory.createSqlType(sqlTypeName, 2);
RelDataType tsWithPrecision3 = typeFactory.createSqlType(sqlTypeName, 3);
// for instance, 8 exceeds max precision for timestamp which is 3
RelDataType tsWithPrecision8 = typeFactory.createSqlType(sqlTypeName, 8);
assertThat(ts, hasToString(sqlTypeName.getName() + "(0)"));
assertThat(ts.getFullTypeString(), is(sqlTypeName.getName() + "(0) NOT NULL"));
assertThat(tsWithoutPrecision, hasToString(sqlTypeName.getName()));
assertThat(tsWithoutPrecision.getFullTypeString(), is(sqlTypeName.getName() + " NOT NULL"));
assertThat(tsWithPrecision0, hasToString(sqlTypeName.getName() + "(0)"));
assertThat(tsWithPrecision0.getFullTypeString(), is(sqlTypeName.getName() + "(0) NOT NULL"));
assertThat(tsWithPrecision1, hasToString(sqlTypeName.getName() + "(1)"));
assertThat(tsWithPrecision1.getFullTypeString(), is(sqlTypeName.getName() + "(1) NOT NULL"));
assertThat(tsWithPrecision2, hasToString(sqlTypeName.getName() + "(2)"));
assertThat(tsWithPrecision2.getFullTypeString(), is(sqlTypeName.getName() + "(2) NOT NULL"));
assertThat(tsWithPrecision3, hasToString(sqlTypeName.getName() + "(3)"));
assertThat(tsWithPrecision3.getFullTypeString(), is(sqlTypeName.getName() + "(3) NOT NULL"));
assertThat(tsWithPrecision8, hasToString(sqlTypeName.getName() + "(3)"));
assertThat(tsWithPrecision8.getFullTypeString(), is(sqlTypeName.getName() + "(3) NOT NULL"));
assertThat(ts != tsWithoutPrecision, is(true));
assertThat(ts == tsWithPrecision0, is(true));
assertThat(tsWithPrecision3 == tsWithPrecision8, is(true));
}
/** Test that the {@code UNKNOWN} type is a {@link BasicSqlType} and remains
* so when nullified. */
@Test void testUnknownCreateWithNullabilityTypeConsistency() {
final SqlTypeFixture f = new SqlTypeFixture();
final RelDataType unknownType = f.typeFactory.createUnknownType();
assertThat(unknownType, instanceOf(BasicSqlType.class));
assertThat(unknownType.getSqlTypeName(), is(SqlTypeName.UNKNOWN));
assertFalse(unknownType.isNullable());
assertThat(unknownType.getFullTypeString(), is("UNKNOWN NOT NULL"));
final RelDataType nullableType =
f.typeFactory.createTypeWithNullability(unknownType, true);
assertThat(nullableType, instanceOf(BasicSqlType.class));
assertThat(nullableType.getSqlTypeName(), is(SqlTypeName.UNKNOWN));
assertTrue(nullableType.isNullable());
assertThat(nullableType.getFullTypeString(), is("UNKNOWN"));
final RelDataType unknownType2 =
f.typeFactory.createTypeWithNullability(nullableType, false);
assertThat(unknownType2, is(unknownType));
assertThat(unknownType2, instanceOf(BasicSqlType.class));
assertThat(unknownType2.getSqlTypeName(), is(SqlTypeName.UNKNOWN));
assertFalse(unknownType2.isNullable());
assertThat(unknownType2.getFullTypeString(), is("UNKNOWN NOT NULL"));
}
}