Skip to content
Merged
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 @@ -31,8 +31,6 @@
import io.airlift.slice.Slice;
import io.airlift.slice.XxHash64;

import java.text.DecimalFormat;

import static com.facebook.presto.common.function.OperatorType.ADD;
import static com.facebook.presto.common.function.OperatorType.BETWEEN;
import static com.facebook.presto.common.function.OperatorType.CAST;
Expand Down Expand Up @@ -76,8 +74,6 @@ public final class DoubleOperators
private static final double MIN_BYTE_AS_DOUBLE = -0x1p7;
private static final double MAX_BYTE_PLUS_ONE_AS_DOUBLE = 0x1p7;

private static final ThreadLocal<DecimalFormat> FORMAT = ThreadLocal.withInitial(() -> new DecimalFormat("0.0###################E0"));

private DoubleOperators()
{
}
Expand Down Expand Up @@ -252,40 +248,9 @@ public static long castToReal(@SqlType(StandardTypes.DOUBLE) double value)
@ScalarOperator(CAST)
@LiteralParameters("x")
@SqlType("varchar(x)")
public static Slice castToVarchar(@LiteralParameter("x") long x, @SqlType(StandardTypes.DOUBLE) double value)
public static Slice castToVarchar(@SqlType(StandardTypes.DOUBLE) double value)
{
String stringValue;

// handle positive and negative 0
if (value == 0e0) {
if (1e0 / value > 0) {
stringValue = "0E0";
}
else {
stringValue = "-0E0";
}
}
else if (Double.isInfinite(value)) {
if (value > 0) {
stringValue = "Infinity";
}
else {
stringValue = "-Infinity";
}
}
else if (Double.isNaN(value)) {
stringValue = "NaN";
}
else {
stringValue = FORMAT.get().format(value);
}

// String is all-ASCII, so String.length() here returns actual code points count
if (stringValue.length() <= x) {
return utf8Slice(stringValue);
}

throw new PrestoException(INVALID_CAST_ARGUMENT, format("Value %s (%s) cannot be represented as varchar(%s)", value, stringValue, x));
return utf8Slice(String.valueOf(value));
}

@ScalarOperator(HASH_CODE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
import io.airlift.slice.Slice;
import io.airlift.slice.XxHash64;

import java.text.DecimalFormat;

import static com.facebook.presto.common.function.OperatorType.ADD;
import static com.facebook.presto.common.function.OperatorType.BETWEEN;
import static com.facebook.presto.common.function.OperatorType.CAST;
Expand All @@ -53,14 +51,12 @@
import static com.facebook.presto.common.function.OperatorType.SUBTRACT;
import static com.facebook.presto.common.function.OperatorType.XX_HASH_64;
import static com.facebook.presto.common.type.RealType.REAL;
import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT;
import static com.facebook.presto.spi.StandardErrorCode.NUMERIC_VALUE_OUT_OF_RANGE;
import static io.airlift.slice.Slices.utf8Slice;
import static java.lang.Float.floatToIntBits;
import static java.lang.Float.floatToRawIntBits;
import static java.lang.Float.intBitsToFloat;
import static java.lang.Math.toIntExact;
import static java.lang.String.format;
import static java.math.RoundingMode.FLOOR;

public final class RealOperators
Expand All @@ -74,8 +70,6 @@ public final class RealOperators
private static final float MIN_BYTE_AS_FLOAT = -0x1p7f;
private static final float MAX_BYTE_PLUS_ONE_AS_FLOAT = 0x1p7f;

private static final ThreadLocal<DecimalFormat> FORMAT = ThreadLocal.withInitial(() -> new DecimalFormat("0.0#####E0"));

private RealOperators()
{
}
Expand Down Expand Up @@ -191,41 +185,9 @@ public static long xxHash64(@SqlType(StandardTypes.REAL) long value)
@ScalarOperator(CAST)
@LiteralParameters("x")
@SqlType("varchar(x)")
public static Slice castToVarchar(@LiteralParameter("x") long x, @SqlType(StandardTypes.REAL) long value)
public static Slice castToVarchar(@SqlType(StandardTypes.REAL) long value)
{
float floatValue = intBitsToFloat((int) value);
String stringValue;

// handle positive and negative 0
if (floatValue == 0.0f) {
if (1.0f / floatValue > 0) {
stringValue = "0E0";
}
else {
stringValue = "-0E0";
}
}
else if (Float.isInfinite(floatValue)) {
if (floatValue > 0) {
stringValue = "Infinity";
}
else {
stringValue = "-Infinity";
}
}
else if (Float.isNaN(floatValue)) {
stringValue = "NaN";
}
else {
stringValue = FORMAT.get().format(Double.parseDouble(Float.toString(floatValue)));
}

// String is all-ASCII, so String.length() here returns actual code points count
if (stringValue.length() <= x) {
return utf8Slice(stringValue);
}

throw new PrestoException(INVALID_CAST_ARGUMENT, format("Value %s (%s) cannot be represented as varchar(%s)", floatValue, stringValue, x));
return utf8Slice(String.valueOf(intBitsToFloat((int) value)));
}

@ScalarOperator(CAST)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
import java.util.Optional;
import java.util.TreeMap;

import static com.facebook.presto.common.type.AbstractVarcharType.UNBOUNDED_LENGTH;
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.DateType.DATE;
Expand Down Expand Up @@ -652,7 +651,7 @@ public static Slice currentTokenAsVarchar(JsonParser parser)
return Slices.utf8Slice(parser.getText());
case VALUE_NUMBER_FLOAT:
// Avoidance of loss of precision does not seem to be possible here because of Jackson implementation.
return DoubleOperators.castToVarchar(UNBOUNDED_LENGTH, parser.getDoubleValue());
return DoubleOperators.castToVarchar(parser.getDoubleValue());
case VALUE_NUMBER_INT:
// An alternative is calling getLongValue and then BigintOperators.castToVarchar.
// It doesn't work as well because it can result in overflow and underflow exceptions for large integral numbers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void testTypeCombinations()
assertFunction("transform(ARRAY [25.6E0, 27.3E0], x -> CAST(x AS BIGINT))", new ArrayType(BIGINT), ImmutableList.of(26L, 27L));
assertFunction("transform(ARRAY [25.6E0, 27.3E0], x -> x + 1.0E0)", new ArrayType(DOUBLE), ImmutableList.of(26.6, 28.3));
assertFunction("transform(ARRAY [25.6E0, 27.3E0], x -> x = 25.6E0)", new ArrayType(BOOLEAN), ImmutableList.of(true, false));
assertFunction("transform(ARRAY [25.6E0, 27.3E0], x -> CAST(x AS VARCHAR))", new ArrayType(createUnboundedVarcharType()), ImmutableList.of("2.56E1", "2.73E1"));
assertFunction("transform(ARRAY [25.6E0, 27.3E0], x -> CAST(x AS VARCHAR))", new ArrayType(createUnboundedVarcharType()), ImmutableList.of("25.6", "27.3"));
assertFunction(
"transform(ARRAY [25.6E0, 27.3E0], x -> MAP(ARRAY[x + 1], ARRAY[true]))",
new ArrayType(mapType(DOUBLE, BOOLEAN)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public void testTypeCombinations()
assertFunction("apply(25.6E0, x -> CAST(x AS BIGINT))", BIGINT, 26L);
assertFunction("apply(25.6E0, x -> x + 1.0E0)", DOUBLE, 26.6);
assertFunction("apply(25.6E0, x -> x = 25.6E0)", BOOLEAN, true);
assertFunction("apply(25.6E0, x -> CAST(x AS VARCHAR))", createUnboundedVarcharType(), "2.56E1");
assertFunction("apply(25.6E0, x -> CAST(x AS VARCHAR))", createUnboundedVarcharType(), "25.6");
assertFunction("apply(25.6E0, x -> MAP(ARRAY[x + 1], ARRAY[true]))", mapType(DOUBLE, BOOLEAN), ImmutableMap.of(26.6, true));

assertFunction("apply(true, x -> if(x, 25, 26))", INTEGER, 25);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,11 @@ public void testTypeCombinations()
assertFunction(
"transform_keys(map(ARRAY [25.5E0, 26.5E0, 27.5E0], ARRAY ['abc', 'def', 'xyz']), (k, v) -> CAST(k AS VARCHAR) || substr(v, 1, 1))",
mapType(VARCHAR, createVarcharType(3)),
ImmutableMap.of("2.55E1a", "abc", "2.65E1d", "def", "2.75E1x", "xyz"));
ImmutableMap.of("25.5a", "abc", "26.5d", "def", "27.5x", "xyz"));
assertFunction(
"transform_keys(map(ARRAY [25.5E0, 26.5E0], ARRAY [ARRAY ['a'], ARRAY ['b']]), (k, v) -> ARRAY [CAST(k AS VARCHAR)] || v)",
mapType(new ArrayType(VARCHAR), new ArrayType(createVarcharType(1))),
ImmutableMap.of(ImmutableList.of("2.55E1", "a"), ImmutableList.of("a"), ImmutableList.of("2.65E1", "b"), ImmutableList.of("b")));
ImmutableMap.of(ImmutableList.of("25.5", "a"), ImmutableList.of("a"), ImmutableList.of("26.5", "b"), ImmutableList.of("b")));

assertFunction(
"transform_keys(map(ARRAY [true, false], ARRAY [25, 26]), (k, v) -> if(k, 2 * v, 3 * v))",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public void testBasic()
assertFunction(
"transform_values(map(ARRAY[1, 2, 3], ARRAY [1.0E0, 1.4E0, 1.7E0]), (k, v) -> map(ARRAY[1, 2, 3], ARRAY['one', 'two', 'three'])[k] || '_' || CAST(v AS VARCHAR))",
mapType(INTEGER, VARCHAR),
ImmutableMap.of(1, "one_1.0E0", 2, "two_1.4E0", 3, "three_1.7E0"));
ImmutableMap.of(1, "one_1.0", 2, "two_1.4", 3, "three_1.7"));
}

@Test
Expand Down Expand Up @@ -177,7 +177,7 @@ public void testTypeCombinations()
assertFunction(
"transform_values(map(ARRAY ['s0', 's1', 's2'], ARRAY [25.5E0, 26.5E0, 27.5E0]), (k, v) -> k || ':' || CAST(v as VARCHAR))",
mapType(createVarcharType(2), VARCHAR),
ImmutableMap.of("s0", "s0:2.55E1", "s1", "s1:2.65E1", "s2", "s2:2.75E1"));
ImmutableMap.of("s0", "s0:25.5", "s1", "s1:26.5", "s2", "s2:27.5"));
assertFunction(
"transform_values(map(ARRAY ['s0', 's2'], ARRAY [false, true]), (k, v) -> if(v, k, CAST(v AS VARCHAR)))",
mapType(createVarcharType(2), VARCHAR),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import com.facebook.presto.metadata.MetadataManager;
import com.facebook.presto.operator.scalar.FunctionAssertions;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.StandardErrorCode;
import com.facebook.presto.spi.WarningCollector;
import com.facebook.presto.spi.relation.CallExpression;
import com.facebook.presto.spi.relation.ConstantExpression;
Expand Down Expand Up @@ -566,10 +565,10 @@ public void testCastToString()
assertOptimizedEquals("cast(-12300000000 as VARCHAR)", "'-12300000000'");

// double
assertOptimizedEquals("CAST(123.0E0 AS varchar)", "'1.23E2'");
assertOptimizedEquals("CAST(-123.0E0 AS varchar)", "'-1.23E2'");
assertOptimizedEquals("CAST(123.456E0 AS varchar)", "'1.23456E2'");
assertOptimizedEquals("CAST(-123.456E0 AS varchar)", "'-1.23456E2'");
assertOptimizedEquals("cast(123.0E0 as VARCHAR)", "'123.0'");
assertOptimizedEquals("cast(-123.0E0 as VARCHAR)", "'-123.0'");
assertOptimizedEquals("cast(123.456E0 as VARCHAR)", "'123.456'");
assertOptimizedEquals("cast(-123.456E0 as VARCHAR)", "'-123.456'");

// boolean
assertOptimizedEquals("cast(true as VARCHAR)", "'true'");
Expand Down Expand Up @@ -623,96 +622,6 @@ public void testCastBigintToBoundedVarchar()
}
}

@Test
public void testCastDoubleToBoundedVarchar()
{
// NaN
assertEvaluatedEquals("CAST(0e0 / 0e0 AS varchar(3))", "'NaN'");
assertEvaluatedEquals("CAST(0e0 / 0e0 AS varchar(50))", "'NaN'");

// Infinity
assertEvaluatedEquals("CAST(DOUBLE 'Infinity' AS varchar(8))", "'Infinity'");
assertEvaluatedEquals("CAST(DOUBLE 'Infinity' AS varchar(50))", "'Infinity'");

assertEvaluatedEquals("CAST(0e0 AS varchar(3))", "'0E0'");
assertEvaluatedEquals("CAST(DOUBLE '0' AS varchar(3))", "'0E0'");
assertEvaluatedEquals("CAST(DOUBLE '-0' AS varchar(4))", "'-0E0'");
assertEvaluatedEquals("CAST(DOUBLE '0' AS varchar(50))", "'0E0'");

assertEvaluatedEquals("CAST(12e0 AS varchar(5))", "'1.2E1'");
assertEvaluatedEquals("CAST(12e2 AS varchar(6))", "'1.2E3'");
assertEvaluatedEquals("CAST(12e-2 AS varchar(6))", "'1.2E-1'");

assertEvaluatedEquals("CAST(12e0 AS varchar(50))", "'1.2E1'");
assertEvaluatedEquals("CAST(12e2 AS varchar(50))", "'1.2E3'");
assertEvaluatedEquals("CAST(12e-2 AS varchar(50))", "'1.2E-1'");

assertEvaluatedEquals("CAST(-12e0 AS varchar(6))", "'-1.2E1'");
assertEvaluatedEquals("CAST(-12e2 AS varchar(6))", "'-1.2E3'");
assertEvaluatedEquals("CAST(-12e-2 AS varchar(7))", "'-1.2E-1'");

assertEvaluatedEquals("CAST(-12e0 AS varchar(50))", "'-1.2E1'");
assertEvaluatedEquals("CAST(-12e2 AS varchar(50))", "'-1.2E3'");
assertEvaluatedEquals("CAST(-12e-2 AS varchar(50))", "'-1.2E-1'");

assertEvaluatedEquals("CAST(12345678.9e0 AS varchar(12))", "'1.23456789E7'");
assertEvaluatedEquals("CAST(0.00001e0 AS varchar(6))", "'1.0E-5'");

// the result value does not fit in the type
assertPrestoExceptionThrownBy("CAST(REAL '12' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value 12.0 (1.2E1) cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL '-12e2' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value -1200.0 (-1.2E3) cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL '0' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value 0.0 (0E0) cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL '0e0' / REAL '0e0' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value NaN (NaN) cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL 'Infinity' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value Infinity (Infinity) cannot be represented as varchar(1)");

assertEvaluatedEquals("CAST(1200000e0 AS varchar(5))", "'1.2E6'");
}

@Test
public void testCastRealToBoundedVarchar()
{
// NaN
assertEvaluatedEquals("CAST(REAL '0e0' / REAL '0e0' AS varchar(3))", "'NaN'");
assertEvaluatedEquals("CAST(REAL '0e0' / REAL '0e0' AS varchar(50))", "'NaN'");

// Infinity
assertEvaluatedEquals("CAST(REAL 'Infinity' AS varchar(8))", "'Infinity'");
assertEvaluatedEquals("CAST(REAL 'Infinity' AS varchar(50))", "'Infinity'");

// incorrect behavior: the string representation is not compliant with the SQL standard
assertEvaluatedEquals("CAST(REAL '0' AS varchar(3))", "'0E0'");
assertEvaluatedEquals("CAST(REAL '-0' AS varchar(4))", "'-0E0'");
assertEvaluatedEquals("CAST(REAL '0' AS varchar(50))", "'0E0'");

assertEvaluatedEquals("CAST(REAL '12' AS varchar(5))", "'1.2E1'");
assertEvaluatedEquals("CAST(REAL '12e2' AS varchar(5))", "'1.2E3'");
assertEvaluatedEquals("CAST(REAL '12e-2' AS varchar(6))", "'1.2E-1'");

assertEvaluatedEquals("CAST(REAL '12' AS varchar(50))", "'1.2E1'");
assertEvaluatedEquals("CAST(REAL '12e2' AS varchar(50))", "'1.2E3'");
assertEvaluatedEquals("CAST(REAL '12e-2' AS varchar(50))", "'1.2E-1'");

assertEvaluatedEquals("CAST(REAL '-12' AS varchar(6))", "'-1.2E1'");
assertEvaluatedEquals("CAST(REAL '-12e2' AS varchar(6))", "'-1.2E3'");
assertEvaluatedEquals("CAST(REAL '-12e-2' AS varchar(7))", "'-1.2E-1'");

assertEvaluatedEquals("CAST(REAL '-12' AS varchar(50))", "'-1.2E1'");
assertEvaluatedEquals("CAST(REAL '-12e2' AS varchar(50))", "'-1.2E3'");
assertEvaluatedEquals("CAST(REAL '-12e-2' AS varchar(50))", "'-1.2E-1'");

assertEvaluatedEquals("CAST(REAL '12345678.9e0' AS varchar(12))", "'1.234568E7'");
assertEvaluatedEquals("CAST(REAL '0.00001e0' AS varchar(12))", "'1.0E-5'");

// the result value does not fit in the type
assertPrestoExceptionThrownBy("CAST(12e0 AS varchar(1))", INVALID_CAST_ARGUMENT, "Value 12.0 (1.2E1) cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(-12e2 AS varchar(1))", INVALID_CAST_ARGUMENT, "Value -1200.0 (-1.2E3) cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(0e0 AS varchar(1))", INVALID_CAST_ARGUMENT, "Value 0.0 (0E0) cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(0e0 / 0e0 AS varchar(1))", INVALID_CAST_ARGUMENT, "Value NaN (NaN) cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(DOUBLE 'Infinity' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value Infinity (Infinity) cannot be represented as varchar(1)");

assertEvaluatedEquals("CAST(REAL '1200000' AS varchar(5))", "'1.2E6'");
}

@Test
public void testCastToBoolean()
{
Expand Down Expand Up @@ -1890,24 +1799,6 @@ private static Object evaluate(Expression expression, boolean deterministic)
return expressionResult;
}

public static void assertPrestoExceptionThrownBy(String expression, StandardErrorCode errorCode, String message)
{
try {
evaluate(expression, true);
fail(format("Expected to throw exception %s", errorCode.toString()));
}
catch (PrestoException e) {
try {
assertEquals(e.getErrorCode(), errorCode.toErrorCode());
assertEquals(e.getMessage(), message);
}
catch (Throwable failure) {
failure.addSuppressed(e);
throw failure;
}
}
}

private static class FailedFunctionRewriter
extends ExpressionRewriter<Object>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -805,14 +805,13 @@ public void testCast()
assertExecute(generateExpression("cast(%s as varchar)", value), VARCHAR, value == null ? null : String.valueOf(value));
}

DecimalFormat doubleFormat = new DecimalFormat("0.0###################E0");
for (Double value : doubleLefts) {
assertExecute(generateExpression("cast(%s as boolean)", value), BOOLEAN, value == null ? null : (value != 0.0 ? true : false));
if (value == null || (value >= Long.MIN_VALUE && value < Long.MAX_VALUE)) {
assertExecute(generateExpression("cast(%s as bigint)", value), BIGINT, value == null ? null : value.longValue());
}
assertExecute(generateExpression("cast(%s as double)", value), DOUBLE, value == null ? null : value);
assertExecute(generateExpression("cast(%s as varchar)", value), VARCHAR, value == null ? null : doubleFormat.format(value));
assertExecute(generateExpression("cast(%s as varchar)", value), VARCHAR, value == null ? null : String.valueOf(value));
}

assertExecute("cast('true' as boolean)", BOOLEAN, true);
Expand Down
Loading