Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReferenceArray;

import static com.google.common.base.Preconditions.checkArgument;

Expand All @@ -42,6 +44,11 @@
* {@link RelDataTypeFactory} which supports SQL types.
*/
public class SqlTypeFactoryImpl extends RelDataTypeFactoryImpl {
private final AtomicReferenceArray<SqlTypeWithPrecision> sqlTypesWithPrecision =
new AtomicReferenceArray<>(SqlTypeName.values().length);
private final AtomicReferenceArray<DecoratedSqlType> decoratedSqlTypes =
new AtomicReferenceArray<>(SqlTypeName.values().length);

//~ Constructors -----------------------------------------------------------

public SqlTypeFactoryImpl(RelDataTypeSystem typeSystem) {
Expand Down Expand Up @@ -72,12 +79,25 @@ public SqlTypeFactoryImpl(RelDataTypeSystem typeSystem) {
assertBasic(typeName);
assert (precision >= 0)
|| (precision == RelDataType.PRECISION_NOT_SPECIFIED);
final int typeIndex = typeName.ordinal();
final @Nullable Charset defaultCharset =
SqlTypeName.CHAR_TYPES.contains(typeName) ? getDefaultCharset() : null;
final @Nullable SqlTypeWithPrecision lastType =
sqlTypesWithPrecision.get(typeIndex);
if (lastType != null
&& lastType.precision == precision
&& Objects.equals(lastType.defaultCharset, defaultCharset)) {
return lastType.type;
}
// Does not check precision when typeName is SqlTypeName#NULL.
RelDataType newType = precision == RelDataType.PRECISION_NOT_SPECIFIED
? new BasicSqlType(typeSystem, typeName)
: new BasicSqlType(typeSystem, typeName, precision);
newType = SqlTypeUtil.addCharsetAndCollation(newType, this);
return canonize(newType);
newType = canonize(newType);
sqlTypesWithPrecision.set(typeIndex,
new SqlTypeWithPrecision(precision, defaultCharset, newType));
return newType;
}

@SuppressWarnings("deprecation") // [CALCITE-6598]
Expand Down Expand Up @@ -168,7 +188,17 @@ public SqlTypeFactoryImpl(RelDataTypeSystem typeSystem) {
RelDataType newType;
if (type instanceof BasicSqlType) {
BasicSqlType sqlType = (BasicSqlType) type;
int index = sqlType.getSqlTypeName().ordinal();
@Nullable DecoratedSqlType cached = decoratedSqlTypes.get(index);
if (cached != null && cached.source == type
&& cached.charset.equals(charset) && cached.collation == collation) {
return cached.result;
}
newType = sqlType.createWithCharsetAndCollation(charset, collation);
newType = canonize(newType);
decoratedSqlTypes.set(index,
new DecoratedSqlType(sqlType, charset, collation, newType));
return newType;
} else if (type instanceof JavaType) {
JavaType javaType = (JavaType) type;
newType =
Expand Down Expand Up @@ -312,6 +342,36 @@ private static void assertBasic(SqlTypeName typeName) {
: "use createSqlIntervalType() instead";
}

/** Most recently created precision for a SQL type that has no scale. */
private static class SqlTypeWithPrecision {
private final int precision;
private final @Nullable Charset defaultCharset;
private final RelDataType type;

private SqlTypeWithPrecision(int precision, @Nullable Charset defaultCharset,
RelDataType type) {
this.precision = precision;
this.defaultCharset = defaultCharset;
this.type = type;
}
}

/** Most recent charset/collation decoration for a SQL type name. */
private static class DecoratedSqlType {
private final BasicSqlType source;
private final Charset charset;
private final SqlCollation collation;
private final RelDataType result;

DecoratedSqlType(BasicSqlType source, Charset charset, SqlCollation collation,
RelDataType result) {
this.source = source;
this.charset = charset;
this.collation = collation;
this.result = result;
}
}

@SuppressWarnings("deprecation") // [CALCITE-6598]
private @Nullable RelDataType leastRestrictiveSqlType(List<RelDataType> types) {
RelDataType resultType = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
import org.apache.calcite.rel.type.RelRecordType;
import org.apache.calcite.rel.type.StructKind;
import org.apache.calcite.sql.SqlCollation;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -38,6 +40,7 @@
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.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

Expand All @@ -46,6 +49,43 @@
*/
class SqlTypeFactoryTest {

@Test void testReuseMostRecentSqlTypeWithPrecision() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType varchar17 = f.typeFactory.createSqlType(SqlTypeName.VARCHAR, 17);
assertSame(varchar17,
f.typeFactory.createSqlType(SqlTypeName.VARCHAR, 17));

RelDataType varchar18 = f.typeFactory.createSqlType(SqlTypeName.VARCHAR, 18);
assertThat(varchar18.getPrecision(), is(18));
assertThat(f.typeFactory.createSqlType(SqlTypeName.VARCHAR, 17).getPrecision(), is(17));

RelDataType char17 = f.typeFactory.createSqlType(SqlTypeName.CHAR, 17);
assertSame(char17, f.typeFactory.createSqlType(SqlTypeName.CHAR, 17));
}

@Test void testCachedCharsetAndCollationDecoration() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataTypeFactory typeFactory = f.typeFactory;
RelDataType source = typeFactory.createSqlType(SqlTypeName.VARCHAR, 17);
RelDataType decorated =
typeFactory.createTypeWithCharsetAndCollation(
source, StandardCharsets.UTF_8, SqlCollation.COERCIBLE);
assertSame(
decorated,
typeFactory.createTypeWithCharsetAndCollation(
source, StandardCharsets.UTF_8, SqlCollation.COERCIBLE));

RelDataType differentCharset =
typeFactory.createTypeWithCharsetAndCollation(
source, StandardCharsets.UTF_16, SqlCollation.COERCIBLE);
assertThat(differentCharset.getCharset(), is(StandardCharsets.UTF_16));
RelDataType otherSource = typeFactory.createSqlType(SqlTypeName.VARCHAR, 18);
RelDataType other =
typeFactory.createTypeWithCharsetAndCollation(
otherSource, StandardCharsets.UTF_8, SqlCollation.COERCIBLE);
assertThat(other.getPrecision(), is(18));
}

@Test void testLeastRestrictiveWithAny() {
SqlTypeFixture f = new SqlTypeFixture();
RelDataType leastRestrictive =
Expand Down
Loading