diff --git a/presto-main/src/main/java/com/facebook/presto/type/CharOperators.java b/presto-main/src/main/java/com/facebook/presto/type/CharOperators.java index b2767e021002..2b38c48450bd 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/CharOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/CharOperators.java @@ -15,6 +15,7 @@ import com.facebook.presto.common.block.Block; import com.facebook.presto.common.type.StandardTypes; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.function.BlockIndex; import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; @@ -26,6 +27,7 @@ import io.airlift.slice.XxHash64; import static com.facebook.presto.common.function.OperatorType.BETWEEN; +import static com.facebook.presto.common.function.OperatorType.CAST; import static com.facebook.presto.common.function.OperatorType.EQUAL; import static com.facebook.presto.common.function.OperatorType.GREATER_THAN; import static com.facebook.presto.common.function.OperatorType.GREATER_THAN_OR_EQUAL; @@ -37,6 +39,8 @@ import static com.facebook.presto.common.function.OperatorType.NOT_EQUAL; import static com.facebook.presto.common.function.OperatorType.XX_HASH_64; import static com.facebook.presto.common.type.Chars.compareChars; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT; +import static java.lang.String.format; public final class CharOperators { @@ -167,4 +171,82 @@ public static boolean indeterminate(@SqlType("char(x)") Slice value, @IsNull boo { return isNull; } + + @LiteralParameters("x") + @ScalarOperator(CAST) + @SqlType(StandardTypes.DOUBLE) + public static double castToDouble(@SqlType("char(x)") Slice slice) + { + try { + return Double.parseDouble(slice.toStringUtf8()); + } + catch (Exception e) { + throw new PrestoException(INVALID_CAST_ARGUMENT, format("Cannot cast '%s' to DOUBLE", slice.toStringUtf8())); + } + } + + @LiteralParameters("x") + @ScalarOperator(CAST) + @SqlType(StandardTypes.REAL) + public static long castToFloat(@SqlType("char(x)") Slice slice) + { + try { + return Float.floatToIntBits(Float.parseFloat(slice.toStringUtf8())); + } + catch (Exception e) { + throw new PrestoException(INVALID_CAST_ARGUMENT, format("Cannot cast '%s' to REAL", slice.toStringUtf8())); + } + } + + @LiteralParameters("x") + @ScalarOperator(CAST) + @SqlType(StandardTypes.BIGINT) + public static long castToBigint(@SqlType("char(x)") Slice slice) + { + try { + return Long.parseLong(slice.toStringUtf8().trim()); + } + catch (Exception e) { + throw new PrestoException(INVALID_CAST_ARGUMENT, format("Cannot cast '%s' to BIGINT", slice.toStringUtf8())); + } + } + + @LiteralParameters("x") + @ScalarOperator(CAST) + @SqlType(StandardTypes.INTEGER) + public static long castToInteger(@SqlType("char(x)") Slice slice) + { + try { + return Integer.parseInt(slice.toStringUtf8().trim()); + } + catch (Exception e) { + throw new PrestoException(INVALID_CAST_ARGUMENT, format("Cannot cast '%s' to INT", slice.toStringUtf8())); + } + } + + @LiteralParameters("x") + @ScalarOperator(CAST) + @SqlType(StandardTypes.SMALLINT) + public static long castToSmallint(@SqlType("char(x)") Slice slice) + { + try { + return Short.parseShort(slice.toStringUtf8().trim()); + } + catch (Exception e) { + throw new PrestoException(INVALID_CAST_ARGUMENT, format("Cannot cast '%s' to SMALLINT", slice.toStringUtf8())); + } + } + + @LiteralParameters("x") + @ScalarOperator(CAST) + @SqlType(StandardTypes.TINYINT) + public static long castToTinyint(@SqlType("char(x)") Slice slice) + { + try { + return Byte.parseByte(slice.toStringUtf8().trim()); + } + catch (Exception e) { + throw new PrestoException(INVALID_CAST_ARGUMENT, format("Cannot cast '%s' to TINYINT", slice.toStringUtf8())); + } + } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java index 1a0d6a8f7710..d31ae0b09eb0 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java @@ -112,6 +112,11 @@ protected void assertFunction(String projection, Type expectedType, Object expec functionAssertions.assertFunction(projection, expectedType, expected); } + protected void assertFunctionString(String projection, Type expectedType, String expected) + { + functionAssertions.assertFunctionString(projection, expectedType, expected); + } + protected void assertFunctionWithError(String projection, Type expectedType, Double expected) { if (expected == null) { diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java index 7a782eb6a801..53186af9f835 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java @@ -24,7 +24,6 @@ import com.facebook.presto.common.type.TimeType; import com.facebook.presto.common.type.TimeZoneKey; import com.facebook.presto.common.type.TimestampType; -import com.facebook.presto.common.type.Type; import com.facebook.presto.spi.StandardErrorCode; import com.facebook.presto.spi.security.ConnectorIdentity; import com.facebook.presto.testing.TestingConnectorSession; @@ -1175,11 +1174,6 @@ public void testIntervalDayToSecondToMilliseconds() assertFunction("to_milliseconds(parse_duration('1d'))", BigintType.BIGINT, DAYS.toMillis(1)); } - private void assertFunctionString(String projection, Type expectedType, String expected) - { - functionAssertions.assertFunctionString(projection, expectedType, expected); - } - private static SqlDate toDate(LocalDate localDate) { return new SqlDate(toIntExact(localDate.toEpochDay())); diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestCharOperators.java b/presto-main/src/test/java/com/facebook/presto/type/TestCharOperators.java index ca51a3ac6749..c22ce2b3276c 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestCharOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestCharOperators.java @@ -17,7 +17,13 @@ import org.testng.annotations.Test; import static com.facebook.presto.common.function.OperatorType.INDETERMINATE; +import static com.facebook.presto.common.type.BigintType.BIGINT; import static com.facebook.presto.common.type.BooleanType.BOOLEAN; +import static com.facebook.presto.common.type.DoubleType.DOUBLE; +import static com.facebook.presto.common.type.IntegerType.INTEGER; +import static com.facebook.presto.common.type.RealType.REAL; +import static com.facebook.presto.common.type.SmallintType.SMALLINT; +import static com.facebook.presto.common.type.TinyintType.TINYINT; public class TestCharOperators extends AbstractTestFunctions @@ -192,4 +198,30 @@ public void testIndeterminate() assertOperator(INDETERMINATE, "CAST(null AS CHAR(3))", BOOLEAN, true); assertOperator(INDETERMINATE, "CHAR '123'", BOOLEAN, false); } + + @Test + public void testCharCast() + { + assertFunction("CAST(CAST('78.95' AS CHAR(5)) AS DOUBLE)", DOUBLE, 78.95); + assertFunction("CAST(CAST(' 45.58 ' AS CHAR(10)) AS DOUBLE)", DOUBLE, 45.58); + assertInvalidCast("CAST(CAST(' Z56 ' AS CHAR(20)) AS DOUBLE)"); + assertFunction("CAST(CAST('45.783' AS CHAR(6)) AS REAL)", REAL, 45.783f); + assertFunction("CAST(CAST(' 45.783 ' AS CHAR(10)) AS REAL)", REAL, 45.783f); + assertInvalidCast("CAST(CAST(' Z56 ' AS CHAR(20)) AS REAL)"); + assertFunctionString("CAST(CAST('6.40282346638528860e+70' AS CHAR(60)) AS REAL)", REAL, "Infinity"); + assertFunction("CAST(CAST('45' AS CHAR(2)) AS BIGINT)", BIGINT, 45L); + assertFunction("CAST(CAST(' 45 ' AS CHAR(10)) AS BIGINT)", BIGINT, 45L); + assertInvalidCast("CAST(CAST(' Z56 ' AS CHAR(20)) AS BIGINT)"); + assertFunction("CAST(CAST('45' AS CHAR(2)) AS INTEGER)", INTEGER, 45); + assertFunction("CAST(CAST('2147483647' AS CHAR(10)) AS INTEGER)", INTEGER, 2147483647); + assertFunction("CAST(CAST(' 45 ' AS CHAR(10)) AS INTEGER)", INTEGER, 45); + assertInvalidCast("CAST(CAST(' Z56 ' AS CHAR(20)) AS INTEGER)"); + assertInvalidCast("CAST(CAST('2147483648' AS CHAR(10)) AS INTEGER)"); // 1 over the max range of integer + assertFunction("CAST(CAST('45' AS CHAR(2)) AS SMALLINT)", SMALLINT, (short) 45); + assertFunction("CAST(CAST(' 45 ' AS CHAR(10)) AS SMALLINT)", SMALLINT, (short) 45); + assertInvalidCast("CAST(CAST(' Z56 ' AS CHAR(20)) AS SMALLINT)"); + assertFunction("CAST(CAST('123' AS CHAR(3)) AS TINYINT)", TINYINT, (byte) 123); + assertFunction("CAST(CAST(' 123 ' AS CHAR(10)) AS TINYINT)", TINYINT, (byte) 123); + assertInvalidCast("CAST(CAST(' Z56 ' AS CHAR(20)) AS TINYINT)"); + } }