diff --git a/core/trino-main/src/main/java/io/trino/type/CharParametricType.java b/core/trino-main/src/main/java/io/trino/type/CharParametricType.java index 50deafe3138b..7b209881d2ce 100644 --- a/core/trino-main/src/main/java/io/trino/type/CharParametricType.java +++ b/core/trino-main/src/main/java/io/trino/type/CharParametricType.java @@ -13,6 +13,7 @@ */ package io.trino.type; +import io.trino.spi.type.CharType; import io.trino.spi.type.ParametricType; import io.trino.spi.type.StandardTypes; import io.trino.spi.type.Type; @@ -22,6 +23,7 @@ import java.util.List; import static io.trino.spi.type.CharType.createCharType; +import static java.lang.Math.toIntExact; public class CharParametricType implements ParametricType @@ -50,6 +52,11 @@ public Type createType(TypeManager typeManager, List parameters) throw new IllegalArgumentException("CHAR length must be a number"); } - return createCharType(parameter.getLongLiteral()); + long length = parameter.getLongLiteral(); + if (length < 0 || length > CharType.MAX_LENGTH) { + throw new IllegalArgumentException("Invalid CHAR length " + length); + } + + return createCharType(toIntExact(length)); } } diff --git a/core/trino-main/src/main/java/io/trino/type/VarcharParametricType.java b/core/trino-main/src/main/java/io/trino/type/VarcharParametricType.java index 2fca08209914..fe18769703a2 100644 --- a/core/trino-main/src/main/java/io/trino/type/VarcharParametricType.java +++ b/core/trino-main/src/main/java/io/trino/type/VarcharParametricType.java @@ -23,6 +23,7 @@ import java.util.List; import static io.trino.spi.type.VarcharType.createUnboundedVarcharType; +import static java.lang.Math.toIntExact; public class VarcharParametricType implements ParametricType @@ -61,6 +62,6 @@ public Type createType(TypeManager typeManager, List parameters) throw new IllegalArgumentException("Invalid VARCHAR length " + length); } - return VarcharType.createVarcharType((int) length); + return VarcharType.createVarcharType(toIntExact(length)); } } diff --git a/core/trino-main/src/test/java/io/trino/operator/scalar/TestStringFunctions.java b/core/trino-main/src/test/java/io/trino/operator/scalar/TestStringFunctions.java index 32f605b2b81e..5338d419b501 100644 --- a/core/trino-main/src/test/java/io/trino/operator/scalar/TestStringFunctions.java +++ b/core/trino-main/src/test/java/io/trino/operator/scalar/TestStringFunctions.java @@ -32,6 +32,7 @@ import static io.trino.spi.StandardErrorCode.FUNCTION_NOT_FOUND; import static io.trino.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; import static io.trino.spi.StandardErrorCode.TOO_MANY_ARGUMENTS; +import static io.trino.spi.StandardErrorCode.TYPE_NOT_FOUND; import static io.trino.spi.type.BigintType.BIGINT; import static io.trino.spi.type.BooleanType.BOOLEAN; import static io.trino.spi.type.CharType.createCharType; @@ -956,7 +957,7 @@ public void testCharConcat() assertFunction("concat('hello na\u00EFve', cast(' world' as char(6)))", createCharType(17), "hello na\u00EFve world"); - assertInvalidFunction("concat(cast('ab ' as char(40000)), cast('' as char(40000)))", "line 1:1: CHAR length must be in range [0, 65536], got 80000"); + assertInvalidFunction("concat(cast('ab ' as char(40000)), cast('' as char(40000)))", TYPE_NOT_FOUND, "line 1:1: Unknown type: char(80000)"); assertFunction("concat(cast(null as char(1)), cast(' ' as char(1)))", createCharType(2), null); } diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/CharType.java b/core/trino-spi/src/main/java/io/trino/spi/type/CharType.java index 6fcff5414dee..f2b77164305a 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/CharType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/CharType.java @@ -17,7 +17,6 @@ import io.airlift.slice.SliceUtf8; import io.airlift.slice.Slices; import io.airlift.slice.XxHash64; -import io.trino.spi.TrinoException; import io.trino.spi.block.Block; import io.trino.spi.block.BlockBuilder; import io.trino.spi.block.BlockBuilderStatus; @@ -30,7 +29,6 @@ import java.util.Optional; import static io.airlift.slice.SliceUtf8.countCodePoints; -import static io.trino.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; import static io.trino.spi.function.OperatorType.COMPARISON_UNORDERED_LAST; import static io.trino.spi.function.OperatorType.EQUAL; import static io.trino.spi.function.OperatorType.XX_HASH_64; @@ -40,6 +38,7 @@ import static io.trino.spi.type.TypeOperatorDeclaration.extractOperatorDeclaration; import static java.lang.Character.MAX_CODE_POINT; import static java.lang.Character.MIN_CODE_POINT; +import static java.lang.Math.toIntExact; import static java.lang.String.format; import static java.lang.invoke.MethodHandles.lookup; import static java.util.Collections.singletonList; @@ -50,16 +49,38 @@ public final class CharType private static final TypeOperatorDeclaration TYPE_OPERATOR_DECLARATION = extractOperatorDeclaration(CharType.class, lookup(), Slice.class); public static final int MAX_LENGTH = 65_536; + private static final CharType[] CACHED_INSTANCES = new CharType[128]; + + static { + for (int i = 0; i < CACHED_INSTANCES.length; i++) { + CACHED_INSTANCES[i] = new CharType(i); + } + } private final int length; private volatile Optional range; + /** + * @deprecated Use {@link #createCharType(int)} instead. + */ + @Deprecated public static CharType createCharType(long length) { + if (length < 0 || length > MAX_LENGTH) { + throw new IllegalArgumentException(format("CHAR length must be in range [0, %s], got %s", MAX_LENGTH, length)); + } + return createCharType(toIntExact(length)); + } + + public static CharType createCharType(int length) + { + if (0 <= length && length < CACHED_INSTANCES.length) { + return CACHED_INSTANCES[length]; + } return new CharType(length); } - private CharType(long length) + private CharType(int length) { super( new TypeSignature( @@ -68,9 +89,9 @@ private CharType(long length) Slice.class); if (length < 0 || length > MAX_LENGTH) { - throw new TrinoException(INVALID_FUNCTION_ARGUMENT, format("CHAR length must be in range [0, %s], got %s", MAX_LENGTH, length)); + throw new IllegalArgumentException(format("CHAR length must be in range [0, %s], got %s", MAX_LENGTH, length)); } - this.length = (int) length; + this.length = length; } public int getLength() diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/VarcharType.java b/core/trino-spi/src/main/java/io/trino/spi/type/VarcharType.java index d2319a9e2c69..ed3e0a24abfc 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/VarcharType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/VarcharType.java @@ -48,6 +48,14 @@ public final class VarcharType public static final int MAX_LENGTH = Integer.MAX_VALUE - 1; public static final VarcharType VARCHAR = new VarcharType(UNBOUNDED_LENGTH); + private static final VarcharType[] CACHED_INSTANCES = new VarcharType[128]; + + static { + for (int i = 0; i < CACHED_INSTANCES.length; i++) { + CACHED_INSTANCES[i] = new VarcharType(i); + } + } + public static VarcharType createUnboundedVarcharType() { return VARCHAR; @@ -59,6 +67,9 @@ public static VarcharType createVarcharType(int length) // Use createUnboundedVarcharType for unbounded VARCHAR. throw new IllegalArgumentException("Invalid VARCHAR length " + length); } + if (length < CACHED_INSTANCES.length) { + return CACHED_INSTANCES[length]; + } return new VarcharType(length); }