Skip to content

Commit

Permalink
Revert "Fix cast from double to varchar: follow the SQL standard in f…
Browse files Browse the repository at this point in the history
…ormatting"

This reverts commit 62b0d7b.
  • Loading branch information
asjadsyed authored and highker committed Mar 24, 2022
1 parent c5863da commit 36fad6a
Show file tree
Hide file tree
Showing 12 changed files with 57 additions and 86 deletions.
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 @@ -254,38 +250,13 @@ public static long castToReal(@SqlType(StandardTypes.DOUBLE) double value)
@SqlType("varchar(x)")
public static Slice castToVarchar(@LiteralParameter("x") long x, @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 stringValue = String.valueOf(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));
throw new PrestoException(INVALID_CAST_ARGUMENT, format("Value %s cannot be represented as varchar(%s)", value, x));
}

@ScalarOperator(HASH_CODE)
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 @@ -566,10 +566,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 @@ -629,38 +629,39 @@ public void testCastDoubleToBoundedVarchar()
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'");
// incorrect behavior: the string representation is not compliant with the SQL standard
assertEvaluatedEquals("CAST(0e0 AS varchar(3))", "'0.0'");
assertEvaluatedEquals("CAST(DOUBLE '0' AS varchar(3))", "'0.0'");
assertEvaluatedEquals("CAST(DOUBLE '-0' AS varchar(4))", "'-0.0'");
assertEvaluatedEquals("CAST(DOUBLE '0' AS varchar(50))", "'0.0'");

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(4))", "'12.0'");
assertEvaluatedEquals("CAST(12e2 AS varchar(6))", "'1200.0'");
assertEvaluatedEquals("CAST(12e-2 AS varchar(4))", "'0.12'");

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(50))", "'12.0'");
assertEvaluatedEquals("CAST(12e2 AS varchar(50))", "'1200.0'");
assertEvaluatedEquals("CAST(12e-2 AS varchar(50))", "'0.12'");

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(5))", "'-12.0'");
assertEvaluatedEquals("CAST(-12e2 AS varchar(7))", "'-1200.0'");
assertEvaluatedEquals("CAST(-12e-2 AS varchar(5))", "'-0.12'");

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(50))", "'-12.0'");
assertEvaluatedEquals("CAST(-12e2 AS varchar(50))", "'-1200.0'");
assertEvaluatedEquals("CAST(-12e-2 AS varchar(50))", "'-0.12'");

// the string representation is compliant with the SQL standard
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'");
// the result value does not fit in the type (also, it is not compliant with the SQL standard)
assertPrestoExceptionThrownBy("CAST(REAL '12' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value 12.0 cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL '-12e2' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value -1200.0 cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL '0' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value 0.0 cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL '0e0' / REAL '0e0' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value NaN cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL 'Infinity' AS varchar(1))", INVALID_CAST_ARGUMENT, "Value Infinity cannot be represented as varchar(1)");
assertPrestoExceptionThrownBy("CAST(REAL '1200000' AS varchar(5))", INVALID_CAST_ARGUMENT, "Value 1200000.0 cannot be represented as varchar(5)");
}

@Test
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
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,12 @@ public void testCastBigintToBoundedVarchar() {
public void testCastDoubleToBoundedVarchar()
{
// the varchar type length is enough to contain the number's representation
assertSimplifies("CAST(0e0 AS varchar(3))", "'0E0'");
assertSimplifies("CAST(-0e0 AS varchar(4))", "'-0E0'");
assertSimplifies("CAST(0e0 AS varchar(3))", "'0.0'");
assertSimplifies("CAST(-0e0 AS varchar(4))", "'-0.0'");
assertSimplifies("CAST(0e0 / 0e0 AS varchar(3))", "'NaN'");
assertSimplifies("CAST(DOUBLE 'Infinity' AS varchar(8))", "'Infinity'");
assertSimplifies("CAST(12e2 AS varchar(5))", "'1.2E3'");
assertSimplifies("CAST(-12e2 AS varchar(50))", "CAST('-1.2E3' AS varchar(50))");
assertSimplifies("CAST(12e2 AS varchar(6))", "'1200.0'");
//assertSimplifies("CAST(-12e2 AS varchar(50))", "CAST('-1200.0' AS varchar(50))");

/// cast from double to varchar fails
assertPrestoExceptionThrownBy("CAST(12e2 AS varchar(3))", INVALID_CAST_ARGUMENT, "Value 1200.0 cannot be represented as varchar(3)");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public void testJsonToArray()
// varchar, json
assertFunction("CAST(JSON '[true, false, 12, 12.3, \"puppies\", \"kittens\", \"null\", \"\", null]' AS ARRAY<VARCHAR>)",
new ArrayType(VARCHAR),
asList("true", "false", "12", "1.23E1", "puppies", "kittens", "null", "", null));
asList("true", "false", "12", "12.3", "puppies", "kittens", "null", "", null));
assertFunction("CAST(JSON '[5, 3.14, [1, 2, 3], \"e\", {\"a\": \"b\"}, null, \"null\", [null]]' AS ARRAY<JSON>)",
new ArrayType(JSON),
ImmutableList.of("5", "3.14", "[1,2,3]", "\"e\"", "{\"a\":\"b\"}", "null", "\"null\"", "[null]"));
Expand Down Expand Up @@ -571,7 +571,7 @@ public void testArrayJoin()
assertFunction("ARRAY_JOIN(ARRAY [1.0, 2.1, 3.3], 'x')", VARCHAR, "1.0x2.1x3.3");
assertFunction("ARRAY_JOIN(ARRAY [1.0, 2.100, 3.3], 'x')", VARCHAR, "1.000x2.100x3.300");
assertFunction("ARRAY_JOIN(ARRAY [1.0, 2.100, NULL], 'x', 'N/A')", VARCHAR, "1.000x2.100xN/A");
assertFunction("ARRAY_JOIN(ARRAY [1.0, DOUBLE '002.100', 3.3], 'x')", VARCHAR, "1.0E0x2.1E0x3.3E0");
assertFunction("ARRAY_JOIN(ARRAY [1.0, DOUBLE '002.100', 3.3], 'x')", VARCHAR, "1.0x2.1x3.3");

assertInvalidFunction("ARRAY_JOIN(ARRAY [ARRAY [1], ARRAY [2]], '-')", INVALID_FUNCTION_ARGUMENT);
assertInvalidFunction("ARRAY_JOIN(ARRAY [MAP(ARRAY [1], ARRAY [2])], '-')", INVALID_FUNCTION_ARGUMENT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,19 +177,19 @@ public void testBetween()
@Test
public void testCastToVarchar()
{
assertFunction("cast(37.7E0 as varchar)", VARCHAR, "3.77E1");
assertFunction("cast(17.1E0 as varchar)", VARCHAR, "1.71E1");
assertFunction("cast(12e2 as varchar(6))", createVarcharType(6), "1.2E3");
assertFunction("cast(12e2 as varchar(50))", createVarcharType(50), "1.2E3");
assertFunction("cast(37.7E0 as varchar)", VARCHAR, "37.7");
assertFunction("cast(17.1E0 as varchar)", VARCHAR, "17.1");
assertFunction("cast(12e2 as varchar(6))", createVarcharType(6), "1200.0");
assertFunction("cast(12e2 as varchar(50))", createVarcharType(50), "1200.0");
assertFunction("cast(12345678.9e0 as varchar(50))", createVarcharType(50), "1.23456789E7");
assertFunction("cast(DOUBLE 'NaN' as varchar(3))", createVarcharType(3), "NaN");
assertFunction("cast(DOUBLE 'Infinity' as varchar(50))", createVarcharType(50), "Infinity");
assertFunction("cast(12e2 as varchar(5))", createVarcharType(5), "1.2E3");
assertInvalidCast("cast(12e2 as varchar(4))", "Value 1200.0 (1.2E3) cannot be represented as varchar(4)");
assertInvalidCast("cast(0e0 as varchar(2))", "Value 0.0 (0E0) cannot be represented as varchar(2)");
assertInvalidCast("cast(-0e0 as varchar(3))", "Value -0.0 (-0E0) cannot be represented as varchar(3)");
assertInvalidCast("cast(0e0 / 0e0 as varchar(2))", "Value NaN (NaN) cannot be represented as varchar(2)");
assertInvalidCast("cast(DOUBLE 'Infinity' as varchar(7))", "Value Infinity (Infinity) cannot be represented as varchar(7)");
assertInvalidCast("cast(12e2 as varchar(5))", "Value 1200.0 cannot be represented as varchar(5)");
assertInvalidCast("cast(12e2 as varchar(4))", "Value 1200.0 cannot be represented as varchar(4)");
assertInvalidCast("cast(0e0 as varchar(2))", "Value 0.0 cannot be represented as varchar(2)");
assertInvalidCast("cast(-0e0 as varchar(3))", "Value -0.0 cannot be represented as varchar(3)");
assertInvalidCast("cast(0e0 / 0e0 as varchar(2))", "Value NaN cannot be represented as varchar(2)");
assertInvalidCast("cast(DOUBLE 'Infinity' as varchar(7))", "Value Infinity cannot be represented as varchar(7)");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ public void testCastToVarchar()
assertFunction("cast(JSON 'null' as VARCHAR)", VARCHAR, null);
assertFunction("cast(JSON '128' as VARCHAR)", VARCHAR, "128");
assertFunction("cast(JSON '12345678901234567890' as VARCHAR)", VARCHAR, "12345678901234567890"); // overflow, no loss of precision
assertFunction("cast(JSON '128.9' as VARCHAR)", VARCHAR, "1.289E2");
assertFunction("cast(JSON '1e-324' as VARCHAR)", VARCHAR, "0E0"); // smaller than minimum subnormal positive
assertFunction("cast(JSON '128.9' as VARCHAR)", VARCHAR, "128.9");
assertFunction("cast(JSON '1e-324' as VARCHAR)", VARCHAR, "0.0"); // smaller than minimum subnormal positive
assertFunction("cast(JSON '1e309' as VARCHAR)", VARCHAR, "Infinity"); // overflow
assertFunction("cast(JSON '-1e309' as VARCHAR)", VARCHAR, "-Infinity"); // underflow
assertFunction("cast(JSON 'true' as VARCHAR)", VARCHAR, "true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ public void testJsonToMap()
mapType(BIGINT, VARCHAR),
asMap(
ImmutableList.of(1L, 2L, 3L, 5L, 8L, 13L, 21L, 34L, 55L),
asList("true", "false", "12", "1.23E1", "puppies", "kittens", "null", "", null)));
asList("true", "false", "12", "12.3", "puppies", "kittens", "null", "", null)));

assertFunction("CAST(JSON '{\"k1\": 5, \"k2\": 3.14, \"k3\":[1, 2, 3], \"k4\":\"e\", \"k5\":{\"a\": \"b\"}, \"k6\":null, \"k7\":\"null\", \"k8\":[null]}' AS MAP<VARCHAR, JSON>)",
mapType(VARCHAR, JSON),
Expand Down

0 comments on commit 36fad6a

Please sign in to comment.